YouTube recently dropped its support for RSS/Atom feeds and recently I was asked if there is a workaround. Or in other words, if it's still possible to use Java to search YouTube by any keyword and retrieve a result of the most relevant video(s). After a bit of tinkering, following is the code that allows searching of YouTube by keyword (or entire sentence) in the currently supported YouTube Data API v3.
First of all, you'll need to get your own key for YouTube Data API v3.
If you don't have it already, go to this url to apply: https://developers.google.com/youtube/registering_an_application#create_project
Prerequisites
Download and import JSOUP library (.jar) to your Java project, we'll using to download JSON from URL. I found that this is the best way to download JSON from any remote url (not just YouTube API). It's a one liner and it not only supports variables such as browser User Agents, but also things like timeouts, etc.
Also don't forget to import JSONTokener, we'll need it in order to parse out Video ID.
Final Code
Finds the most relevant YouTube video for any given search string:
import org.jsoup.Jsoup;
import org.json.JSONTokener;
String keyword = "how to make apple pie filling";
keyword = keyword.replace(" ", "+");
String url = "https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&order=rating&q=" + keyword + "&key=YOUR_YOUTUBE_API_KEY";
Document doc = Jsoup.connect(url).timeout(10 * 1000).get();
String getJson = doc.text();
JSONObject jsonObject = (JSONObject) new JSONTokener(getJson ).nextValue();
System.out.println(jsonObject.getString("videoId"));
For a search query: "how to make apple pie filling", above code would return following result:
f4wW-lrf3-s
Or this particular video:
https://www.youtube.com/watch?v=f4wW-lrf3-s
As you can see, it's as simple as:
- typing your keyword
- grabbing JSON result from YouTube Data API (btw. I've built my own url using [this tool](https://developers.google.com/apis-explorer/#p/youtube/v3/youtube.search.list) to look for the most relevant single result)
- and finally parsing videoId attribute
Above code is tested and working quite nicely.
Just for the reference, this is what JSON result for the above code example looks like:
{
"kind": "youtube#searchListResponse",
"etag": "\"q5k97EMVGxODeKcDgp8gnMu79wM/pVtv8dVWGGCmO2H_Mdfu_CiMvFA\"",
"nextPageToken": "CAEQAA",
"regionCode": "CA",
"pageInfo": {
"totalResults": 349477,
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#searchResult",
"etag": "\"q5k97EMVGxODeKcDgp8gnMu79wM/UWEnOuLqTx2opToB7YgCPtRbTvw\"",
"id": {
"kind": "youtube#video",
"videoId": "f4wW-lrf3-s"
},
"snippet": {
"publishedAt": "2014-12-05T19:42:45.000Z",
"channelId": "UC6aKglbMTPNDbcfixTdh9XQ",
"title": "Homemade apple pie recipe",
"description": "Here's Dawn's recipe for homemade apple pie, enjoy! Ingredients: -5-6 large granny smith apples -1 cup of sugar -nutmeg -cinnamon -2 tbsp of flour -1/4 cup of ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/f4wW-lrf3-s/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/f4wW-lrf3-s/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/f4wW-lrf3-s/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "",
"liveBroadcastContent": "none"
}
}
]
}
As you can see, you can use my example to grab anything that's passed in JSON, not only Video ID, but also Title, Thumbnail, Description, Channel ID, etc.
I hope you find this useful.
--
So, above was my solution, whereas this is what Google suggest doing:
public class Search {
/**
* Define a global variable that identifies the name of a file that
* contains the developer's API key.
*/
private static final String PROPERTIES_FILENAME = "youtube.properties";
private static final long NUMBER_OF_VIDEOS_RETURNED = 1;
/**
* Define a global instance of a Youtube object, which will be used
* to make YouTube Data API requests.
*/
private static YouTube youtube;
/**
* Initialize a YouTube object to search for videos on YouTube. Then
* display the name and thumbnail image of each video in the result set.
*
* @param args command line args.
*/
public static void main(String[] args) {
// Read the developer key from the properties file.
Properties properties = new Properties();
try {
InputStream in = Search.class.getResourceAsStream("/" + PROPERTIES_FILENAME);
properties.load(in);
} catch (IOException e) {
System.err.println("There was an error reading " + PROPERTIES_FILENAME + ": " + e.getCause()
+ " : " + e.getMessage());
System.exit(1);
}
try {
// This object is used to make YouTube Data API requests. The last
// argument is required, but since we don't need anything
// initialized when the HttpRequest is initialized, we override
// the interface and provide a no-op function.
youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, new HttpRequestInitializer() {
public void initialize(HttpRequest request) throws IOException {
}
}).setApplicationName("youtube-cmdline-search-sample").build();
// Prompt the user to enter a query term.
String queryTerm = getInputQuery();
// Define the API request for retrieving search results.
YouTube.Search.List search = youtube.search().list("id,snippet");
// Set your developer key from the Google Developers Console for
// non-authenticated requests. See:
// https://console.developers.google.com/
String apiKey = properties.getProperty("youtube.apikey");
search.setKey(apiKey);
search.setQ(queryTerm);
// Restrict the search results to only include videos. See:
// https://developers.google.com/youtube/v3/docs/search/list#type
search.setType("video");
// To increase efficiency, only retrieve the fields that the
// application uses.
search.setFields("items(id/kind,id/videoId,snippet/title,snippet/thumbnails/default/url)");
search.setMaxResults(NUMBER_OF_VIDEOS_RETURNED);
// Call the API and print results.
SearchListResponse searchResponse = search.execute();
List<SearchResult> searchResultList = searchResponse.getItems();
if (searchResultList != null) {
prettyPrint(searchResultList.iterator(), queryTerm);
}
} catch (GoogleJsonResponseException e) {
System.err.println("There was a service error: " + e.getDetails().getCode() + " : "
+ e.getDetails().getMessage());
} catch (IOException e) {
System.err.println("There was an IO error: " + e.getCause() + " : " + e.getMessage());
} catch (Throwable t) {
t.printStackTrace();
}
}
/*
* Prompt the user to enter a query term and return the user-specified term.
*/
private static String getInputQuery() throws IOException {
String inputQuery = "";
System.out.print("Please enter a search term: ");
BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
inputQuery = bReader.readLine();
if (inputQuery.length() < 1) {
// Use the string "YouTube Developers Live" as a default.
inputQuery = "YouTube Developers Live";
}
return inputQuery;
}
/*
* Prints out all results in the Iterator. For each result, print the
* title, video ID, and thumbnail.
*
* @param iteratorSearchResults Iterator of SearchResults to print
*
* @param query Search query (String)
*/
private static void prettyPrint(Iterator<SearchResult> iteratorSearchResults, String query) {
System.out.println("\n=============================================================");
System.out.println(
" First " + NUMBER_OF_VIDEOS_RETURNED + " videos for search on \"" + query + "\".");
System.out.println("=============================================================\n");
if (!iteratorSearchResults.hasNext()) {
System.out.println(" There aren't any results for your query.");
}
while (iteratorSearchResults.hasNext()) {
SearchResult singleVideo = iteratorSearchResults.next();
ResourceId rId = singleVideo.getId();
// Confirm that the result represents a video. Otherwise, the
// item will not contain a video ID.
if (rId.getKind().equals("youtube#video")) {
Thumbnail thumbnail = singleVideo.getSnippet().getThumbnails().getDefault();
System.out.println(" Video Id" + rId.getVideoId());
System.out.println(" Title: " + singleVideo.getSnippet().getTitle());
System.out.println(" Thumbnail: " + thumbnail.getUrl());
System.out.println("\n-------------------------------------------------------------\n");
}
}
}
}