Believe it or not, YouTube is the second-most popular search engine in the world. It’s safe to say that people go there for information.

That’s why you might have a business need to integrate your Spring Boot app with YouTube. Fortunately, YouTube offers a Java API that makes integration really easy.

In this article, we’ll go over how to use the YouTube Java API within your own Spring Boot app. As is usually the case, you can view the code on GitHub.

Let;s get started!

 

Get a Google API Key

Before you do anything, you’re going to need a Google API Key. You can grab that at the Google Developers Console.

Once you’re there, click “Credentials” on the left-hand sidebar.

api-manager

 

On the “Credentials” main screen, select “API Key” under the “Create Credentials” blue drop-down.

credentials

 

Click that button and the tool will create an API key for you instantly. Make a note of that key because you’ll need it later.

 

Enable the YouTube API

Since Google has countless APIs, you’ll need to specify which one you want to use. In this case, you need to select the YouTube API.

Fortunately, that’s easy to do. Just click on “Dashboard” on the right-hand sidebar and click on “Enable API” at the very top of the main screen.

dashboard

 

For the purposes of this integration, you want the YouTube Data API. It’s towards the end of the Library screen because the APIs are sorted alphabetically.

Once you’re done with that, you’re ready to start coding!

 

Grab the Google Dependencies

Google has provided a whole library of JAR files that make it easy to use its APIs. You’re going to need a few of those JAR files for this tutorial.

Here are the relevant lines to add to your POM file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-youtube</artifactId>
    <version>v3-rev183-1.22.0</version>
</dependency>
<dependency>
    <groupId>com.google.http-client</groupId>
    <artifactId>google-http-client-jackson2</artifactId>
    <version>1.22.0</version>
</dependency>
<dependency>
    <groupId>com.google.collections</groupId>
    <artifactId>google-collections</artifactId>
    <version>1.0</version>
</dependency>

Note that by the time you’re reading this (because the Internet is forever), you might need later versions than the versions specified here.

 

The YouTubeDemoController Class

You’re only going to need one controller class for this tutorial. It’s going to handle both GET and POST requests.

Check out the source code for the GET request below:

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value = "/youtubeDemo", method=RequestMethod.GET)
public String youtubeDemo(Model model) {
    //instantiate an empty address object
    YoutubeSearchCriteria youtubeSearchCriteria = new YoutubeSearchCriteria();
         
    //put the object in the model
    model.addAttribute("youtubeSearchCriteria", youtubeSearchCriteria);
         
    //get out
    return "youtubeDemo";
}

As you can see, the method maps to a GET request with the URL path /youtubeDemo.

There’s nothing complicated going on in the method itself. It starts off by instantiating an empty YoutubeSearchCriteria object and then stuffs it in the model.

The YoutubeSearchCriteria class is just a bean that holds the search criteria the user will enter. For the purposes of this tutorial, that class only has one field: queryTerm.

Finally, the method returns the string “youtubeDemo” which maps to the file youtubeDemo.html. That’s an XHTML/Thymeleaf file that renders the web page.

 

The Form

You want input from the user, so naturally you’ll need an HTML form. Unsurprisingly, you’ll include that form in the youtubeDemo.html file.

Here’s what the code looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<form id="employeeForm" th:action="@{/youtubeDemo}" th:object="${youtubeSearchCriteria}" method="POST">
    <div class="form-body">
        <div>
            <div class="mt-element-ribbon bg-grey-steel">
                <div class="ribbon ribbon-color-success uppercase">Search Criteria</div>
                <p class="ribbon-content">Please enter a keyword or phrase to search for.</p>
            </div>
        </div>
  
        <div class="form-group" th:classappend="${#fields.hasErrors('queryTerm')} ? has-error : ''">
            <label>Search:</label>
            <div th:classappend="${#fields.hasErrors('queryTerm')} ? 'input-icon right' : ''">
                <i th:if="${#fields.hasErrors('queryTerm')}" class="fa fa-exclamation tooltips" data-original-title="please enter a valid search term" data-container="body"></i>
                <input type="text" class="form-control" maxlength="64" th:field="*{queryTerm}" placeholder="For example: tom brady"/>
                <span th:if="${#fields.hasErrors('queryTerm')}" class="help-block" th:errors="*{queryTerm}"></span>
           </div>
        </div>
  
     </div>
     <div class="form-actions">
         <button type="submit" class="btn blue">Search YouTube</button>
     </div>
</form>

That code looks quite a bit more complicated than it really is.

As you can see in the top <form> element, it’s mapping the YoutubeSearchCriteria object to the form itself. That way, when the user submits the form, the object will be updated accordingly.

Go a little farther down in the code and you’ll see its only <input> element. In this case, that’s a text input field that maps to the queryTerm field in the YoutubeSearchCriteria object.

The form also validates user input, but it’s beyond the score of this tutorial to go into that. If you want to learn more about form field validation, check out the the tutorial on that subject.

The button at the bottom of the code block submits the form. As you can see from looking at the <form> element, clicking on that button will POST the form to the /youtubeDemo path.

Here’s what the page looks like:

frontpage

Be sure to view the full HTML source to see everything that goes into displaying that page.

 

The YouTubeDemoController Class, Revisited

Recall from above that the single controller class is handling both the GET and the POST request for the same URL (/youtubeDemo). In this section, we’ll look at the code that handles the POST request.

Here’s what that code looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RequestMapping(value = "/youtubeDemo", method=RequestMethod.POST)
public String formSubmit(@Valid YoutubeSearchCriteria youtubeSearchCriteria, BindingResult bindingResult, Model model) {
    //check for errors
    if (bindingResult.hasErrors()) {
        return "youtubeDemo";
    }
         
    //get the list of YouTube videos that match the search term
    List<YouTubeVideo> videos = youtubeService.fetchVideosByQuery(youtubeSearchCriteria.getQueryTerm());
         
    if (videos != null && videos.size() > 0) {
        model.addAttribute("numberOfVideos", videos.size());
    } else {
        model.addAttribute("numberOfVideos", 0);
    }
         
    //put it in the model
    model.addAttribute("videos", videos);
         
    //add the criteria to the model as well
    model.addAttribute("youtubeSearchCriteria", youtubeSearchCriteria);
         
    //get out
    return "showYoutubeResults";
}

The method starts off by looking for binding errors. If it finds any, it kicks the user back the page.

Following that, the next line of code fetches the videos that match the search term. We’ll look at the code that handles that in a bit more detail later.

The video objects that matched are returned in a list. That list is placed in the Model object so the app can show data from that list to the user.

Finally, the method returns the string “showYoutubeResults,” which means the the app will display the showYoutubeResults.html page.

 

The YouTubeService Class

As you can see from the code above, the method that handles POST request relies an instance of the YouTubeService class. Check out the code for that class here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public List<YouTubeVideo> fetchVideosByQuery(String queryTerm) {
    List<YouTubeVideo> videos = new ArrayList<YouTubeVideo>();
     
    try {
        //instantiate youtube object
        YouTube youtube = getYouTube();
     
        //define what info we want to get
        YouTube.Search.List search = youtube.search().list("id,snippet");
             
        //set our credentials
        String apiKey = "[your API key here]";
        search.setKey(apiKey);
             
        //set the search term
        search.setQ(queryTerm);
     
        //we only want video results
        search.setType("video");
     
        //set the fields that we're going to use       
        search.setFields ("items(id/kind,id/videoId,snippet/title,snippet/description,snippet/publishedAt,snippet/thumbnails/default/url)");
             
        //set the max results
        search.setMaxResults(MAX_SEARCH_RESULTS);
             
        DateFormat df = new SimpleDateFormat("MMM dd, yyyy");
             
        //perform the search and parse the results
        SearchListResponse searchResponse = search.execute();
        List<SearchResult> searchResultList = searchResponse.getItems();
        if (searchResultList != null) {
            for (SearchResult result : searchResultList) {
                YouTubeVideo video = new YouTubeVideo();
                video.setTitle(result.getSnippet().getTitle());
                video.setUrl(buildVideoUrl(result.getId().getVideoId()));
                video.setThumbnailUrl(result.getSnippet().getThumbnails().getDefault().getUrl());
                video.setDescription(result.getSnippet().getDescription());
                    
                //parse the date
                DateTime dateTime = result.getSnippet().getPublishedAt();
                Date date = new Date(dateTime.getValue());
                String dateString = df.format(date);
                video.setPublishDate(dateString);
                        
                videos.add(video);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
         
    return videos;
}

As you may have noticed, the code block above relies on some of the libraries you included in the POM file.

The method accepts a String object that contains the search term and then uses the YouTube API to find videos that match the term.

It starts out by instantiating a YouTube object. We’ll cover that method in a moment.

Next is some “upscale” Java that defines the data required by the app. That “id,snippet” string tells the API that the app is looking for the ID and the snippet of the YouTube videos that match the search term.

The ID is self-explanatory. The snippet contains info about the video, such as its description thumbnail URL.

The next line of code specifies the API key. Be sure to substitute your own API key in that line.

Next, the code sets the key field in the search object to the value of the API key.

Following that, the code sets the q field in the search object to the value of the search term.

Next, the code sets the search type to “video.” That’s because the user only wants to see videos.

After that, the code specifies the fields that can be accessed from the returned objects. As you can see, the fields are specified in a tree format and reflect the kind of data that the app will ultimately show to the user.

Then the code sets the maximum search results to return. In this case, it’s using a constant that’s set to 5.

Next, the code instantiates a DateFormat object. That’s the date format that the app will show to users on the front end.

Then, the code executes the search and iterates over the response.

Note that the code instantiates a YouTubeVideo object for each video that’s returned. That object is not part of one of the Google libraries. It’s a simple bean that’s part of the app code base.

Basically, that for/next loop creates a List of YouTubeVideo objects that contain relevant info about the videos that matched the user’s search term. That List object is then returned to the controller class so it can be stuffed in the Model object and finally returned to the view.

 

Instantiating the YouTube Object

It’s not quite as easy to instantiate the YouTube object as it is to instantiate a simpler Java object, like a bean.

Check out the code below:

1
2
3
4
5
6
private YouTube getYouTube() {
    YouTube youtube = new YouTube.Builder(new NetHttpTransport(), new JacksonFactory(),
    (request) -> {}).setApplicationName("youtube-spring-boot-demo").build();
  
    return youtube;
}

As you can see, the code essentially uses a factory to build the YouTube object.

That factory requires the name of the app as part of its input. In this case, the app is called “youtube-spring-boot-demo.”

 

Displaying the Results

Once you’re done retrieving the video objects, it’s time to extract info from each object and display it to the user.

Recall that the POST method uses the YouTubeService class to retrieve the video objects. That service returns a List of YouTubeVideo objects that are placed in the Model object. The view layer uses the Model object to display info about the videos to the user.

The relevant code is found in the showYoutubeResults.html file. Here’s what the relevant part of that code looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="mt-list-container list-news ext-1">
<ul>
<li class="mt-list-item" th:each="video,iterationStatus : ${videos}">
<div class="list-icon-container">
<a th:href="${video.url}" target="_blank">
<i class="fa fa-angle-right"></i>
</a>
</div>
<div class="list-thumb">
<a th:href="${video.url}" target="_blank">
<img alt="" th:src="${video.thumbnailUrl}" />
</a>
</div>
<div class="list-datetime bold uppercase font-blue" th:text="${video.publishDate}"></div>
<div class="list-item-content">
<h4 class="uppercase">
<a th:href="${video.url}" target="_blank" th:text="${video.title}"></a>
</h4>
<p th:text="${video.description}"></p>
</div>
</li>
</ul>
</div>

Once again, there’s a mix of HTML and Thymeleaf in the code.

Pay particular attention to the th:each attribute in the <li> element. That’s the start of an iteration.

You can see that it’s set to the value of a request attribute named “videos.” That was set in the controller class.

Basically, that line is saying, “I’m going to create a new <li> element for each video in the list of videos.”

After that, everything is just a matter of user experience. For each video in the list, the code displays its thumbnail, description, and upload date. The title and thumbnail are also clickable so the user can watch the video on YouTube.

Here’s what a typical response list looks like:

youtube-results

 

Wrapping It Up

Some of those Google APIs can be fairly complicated. That doesn’t mean that they’re impossible to use, though.

Just look at examples (often provided by Google, sometimes provided on this website) and swipe the code for your own integration. Sure, you’ll need to tweak it a bit, but at least you have a starting point.

Feel free to check out the complete source code on GitHub.

Have fun!