javayoutubeyoutube-apiyoutube-data-apiyoutube-channels

Generating list of video uploads for a particular YouTube subscription


I am trying to generate a list of videos from a channel that I am subscribed to.

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.PlaylistItemListResponse;
import com.google.api.services.youtube.model.SubscriptionListResponse;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.Collections;

public class ApiExample {
    private static final String CLIENT_SECRETS= "client_secret.json";
    private static final Collection<String> SCOPES =
            Collections.singletonList("https://www.googleapis.com/auth/youtube.readonly");

    private static final String APPLICATION_NAME = "Stack Overflow MRE";
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

    /**
     * Create an authorized Credential object.
     *
     * @return an authorized Credential object.
     * @throws IOException in the event that the client_secrets.json file is not found
     */
    public static Credential authorize(final NetHttpTransport httpTransport) throws IOException {
        // Load client secrets.
        InputStream in = ApiExample.class.getResourceAsStream(CLIENT_SECRETS);
        GoogleClientSecrets clientSecrets =
                GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow =
                new GoogleAuthorizationCodeFlow.Builder(httpTransport, JSON_FACTORY, clientSecrets, SCOPES)
                        .build();
        return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
    }

    /**
     * Build and return an authorized API client service.
     *
     * @return an authorized API client service
     * @throws GeneralSecurityException, IOException
     */
    public static YouTube getService() throws GeneralSecurityException, IOException {
        final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        Credential credential = authorize(httpTransport);
        return new YouTube.Builder(httpTransport, JSON_FACTORY, credential)
                .setApplicationName(APPLICATION_NAME)
                .build();
    }

    /**
     * Call function to create API service object. Define and
     * execute API request. Print API response.
     *
     * @throws GeneralSecurityException, IOException, GoogleJsonResponseException
     */
    public static void main(String[] args)
            throws GeneralSecurityException, IOException {
        YouTube youtubeService = getService();
        // Define and execute the API request
        YouTube.Subscriptions.List request = youtubeService.subscriptions()
                .list("snippet");
        SubscriptionListResponse response = request.setMine(true).setMaxResults(1L).execute();
        String channelId = response.getItems().get(0).getSnippet().getResourceId().getChannelId();

        YouTube.PlaylistItems.List playListRequest = youtubeService.playlistItems().list("snippet");
        PlaylistItemListResponse playlistResponse = playListRequest.setPlaylistId(channelId).execute();
        playlistResponse.getItems().forEach(System.out::println);
    }
}

I have read YouTube API to fetch all videos on a channel, however my question is a little different because I am trying to get the list of videos from a Subscription, rather than from a Channel.

I attempted to isolate the Channel ID in order to follow the instructions in the video from Google Developers about how to do this. I used String channelId = response.getItems().get(0).getSnippet().getResourceId().getChannelId();

However, when I run the above code, which should print out a list of videos from that Channel in JSON notation, I see this error instead:

Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found
{
  "code" : 404,
  "errors" : [ {
    "domain" : "youtube.playlistItem",
    "location" : "playlistId",
    "locationType" : "parameter",
    "message" : "The playlist identified with the request's <code>playlistId</code> parameter cannot be found.",
    "reason" : "playlistNotFound"
  } ],
  "message" : "The playlist identified with the request's <code>playlistId</code> parameter cannot be found."
}
    at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:150)
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1067)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
    at ApiExample.main(ApiExample.java:93)

Evidently, I have not correctly isolated the Channel ID, or else I have misunderstood the video. What is the correct way to get the videos from a YouTube subscription?

Edit

The following change in my main method worked:

public static void main(String[] args)
            throws GeneralSecurityException, IOException {
    YouTube youtubeService = getService();
    // Define and execute the API request
    YouTube.Subscriptions.List request = youtubeService.subscriptions()
            .list("snippet")
    SubscriptionListResponse response = request.setMine(true).setMaxResults(1L).execute();
    String channelId = response.getItems().get(0).getSnippet().getResourceId().getChannelId();

    YouTube.Channels.List channelRequest = youtubeService.channels().list("contentDetails");
    ChannelListResponse channelResponse = channelRequest.setId(channelId).execute();
    String playListID = channelResponse.getItems().get(0).getContentDetails().getRelatedPlaylists().getUploads();

    YouTube.PlaylistItems.List playListRequest = youtubeService.playlistItems().list("snippet");
    PlaylistItemListResponse playlistResponse = playListRequest.setPlaylistId(playListID).execute();
    playlistResponse.getItems().forEach(System.out::println);
}

Solution

  • The problem with your code above boils down to the following doc entry:

    playlistId (string)

    The playlistId parameter specifies the unique ID of the playlist for which you want to retrieve playlist items. Note that even though this is an optional parameter, every request to retrieve playlist items must specify a value for either the id parameter or the playlistId parameter.

    Things should come into light by now: playlistId is the ID of a playlist, hence cannot be the ID of a channel.

    But for listing all uploaded videos of a given channel (identified by its ID), one has to do the following:

    Invoke the PlaylistItems.list API endpoint queried with the parameter playlistId set to the ID of that channel's uploads playlist.

    This latter ID may very easily be obtained by invoking the Channels.list endpoint queried with the parameter id set to your channel's ID.

    The uploads playlist ID is then to be found within the endpoint's JSON response as value of the property:

    items[0].contentDetails.relatedPlaylists.uploads.

    Translated to Java, this property path would become the following chain of getters, ending with getUploads:

    .getItems().get(0).getContentDetails().getRelatedPlaylists().getUploads().

    Note that for a given channel, you need to obtain the uploads playlist ID only once, then use it as many times as you wish.

    Usually, a channel ID and its corresponding uploads playlist ID are related by s/^UC([0-9a-zA-Z_-]{22})$/UU\1/.