zvi
zvi

Reputation: 13

How to get all videos from my own YouTube channel via YouTube Data API v3

I need to get the ID and title for all videos from my own YouTube channel. Some of videos are unlisted, but I want to get them too. I use this version of the client library:

<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-youtube</artifactId>
    <version>v3-rev222-1.25.0</version>
</dependency>

According to the documentation I use this Java code:

YouTube.Search.List request = youtubeService.search()
            .list("id,snippet");
SearchListResponse response = request.setChannelId(channelId)
            .setForMine(true)
            .setMaxResults(50L)
            .setType("video")
            .execute();

But got this exception:

400 Bad Request
GET https://www.googleapis.com/youtube/v3/search?channelId=channelId&forMine=true&maxResults=50&part=id,snippet&type=video
{
  "code" : 400,
  "errors" : [ {
    "domain" : "youtube.search",
    "location" : "parameters.",
    "locationType" : "other",
    "message" : "The request contains an invalid combination of search filters and/or restrictions. Note that you must set the <code>type</code> parameter to <code>video</code> if you set either the <code>forContentOwner</code> or <code>forMine</code> parameters to <code>true</code>. You must also set the <code>type</code> parameter to <code>video</code> if you set a value for the <code>eventType</code>, <code>videoCaption</code>, <code>videoCategoryId</code>, <code>videoDefinition</code>, <code>videoDimension</code>, <code>videoDuration</code>, <code>videoEmbeddable</code>, <code>videoLicense</code>, <code>videoSyndicated</code>, or <code>videoType</code> parameters.",
    "reason" : "invalidSearchFilter"
  } ],
  "message" : "The request contains an invalid combination of search filters and/or restrictions. Note that you must set the <code>type</code> parameter to <code>video</code> if you set either the <code>forContentOwner</code> or <code>forMine</code> parameters to <code>true</code>. You must also set the <code>type</code> parameter to <code>video</code> if you set a value for the <code>eventType</code>, <code>videoCaption</code>, <code>videoCategoryId</code>, <code>videoDefinition</code>, <code>videoDimension</code>, <code>videoDuration</code>, <code>videoEmbeddable</code>, <code>videoLicense</code>, <code>videoSyndicated</code>, or <code>videoType</code> parameters."
}

Moreover I got the same error using interactive tool on this page:

https://developers.google.com/youtube/v3/docs/search/list?apix=true.

If I remove .setForMine(true) it works fine, but doesn't give me unlisted videos (gives me only public videos).

Are there any possibilities to get the ID and title of all videos (unlisted included) from my own channel via the API?

Upvotes: 0

Views: 1896

Answers (1)

stvar
stvar

Reputation: 6985

The short answer to your question is: indeed, there is an API that will provide all video metadata of your channel, including the metadata of unlisted videos.

For the longer answer, just bear with me for a while:

First thing, do note that a given video has a privacy status of the following kinds:

status.privacyStatus (string)
The video's privacy status. Valid values for this property are:

  • private
  • public
  • unlisted

For to obtain the IDs of all videos uploaded by your channel, irrespective of their privacy status, you'll have to invoke the PlaylistItems.list API endpoint queried with the parameter playlistId set to the ID of your channel's uploads playlist, while issuing an OAuth authorized request (that is: passing to the API a valid access token; using only an API key will make the endpoint return only public videos):

List<String> scopes = Lists.newArrayList(
    "https://www.googleapis.com/auth/youtube.readonly"); // or ".../auth/youtube"

Credential credential = Auth.authorize(scopes, "myuploads");

youtube = new YouTube.Builder(
    Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, credential)
    .setApplicationName("myuploads")
    .build();

List<String> videoIdList = new ArrayList<String>();

YouTube.PlaylistItems.List playlistItemRequest =
    youtube.playlistItems().list("contentDetails");
playlistItemRequest.setFields("nextPageToken,items/contentDetails/videoId");
playlistItemRequest.setMaxResults(50);
playlistItemRequest.setPlaylistId(uploadsPlaylistId);

String nextToken = "";
do {
    playlistItemRequest.setPageToken(nextToken);
    PlaylistItemListResponse playlistItemResult = playlistItemRequest.execute();

    for (PlaylistItem playlistItem: playlistItemResult.getItems())
        videoIdList.add(playlistItem.getContentDetails().getVideoId());

    nextToken = playlistItemResult.getNextPageToken();
} while (nextToken != null);

Now, the Videos.list API endpoint will provide you all the metadata of the videos of which IDs are collected by videoIdList.

List<Video> videoList = new ArrayList<Video>();
while (videoIdList.size() > 0) {
    int endIndex = videoIdList.size() < 50 ? videoIdList.size() : 50;
    List<String> subList = videoIdList.subList(0, endIndex);

    YouTube.Videos.List videosRequest =
        youtube.videos().list("id,contentDetails,status,snippet, statistics");
    videosRequest.setId(String.join(",", subList));
    VideoListResponse videosResponse = videoRequest.execute();

    videoList.AddAll(videosResponse.getItems());

    subList.clear();
}

Note that, if videoIdList is of size N at the beginning of the loop above -- since the parameter id of Videos.list endpoint can be specified as a comma-separated list of video IDs --, the loop code reduces the number of calls to Videos.list endpoint from N to Math.floor(N / 50) + (N % 50 ? 1 : 0) by appropriately using the feature of id just mentioned.

The uploads playlist ID -- uploadsPlaylistId -- may very easily be obtained by invoking the Channels.list endpoint queried with the parameter id set to your channel's ID, or, otherwise, queried with the parameter mine set to true:

YouTube.Channels.List channelRequest =
    youtube.channels().list("contentDetails");
channelRequest.setMine(true);
channelRequest.setFields("items/contentDetails/relatedPlaylists/uploads");
ChannelListResponse channelResult = channelRequest.execute();

Note that above I used the fields parameter for to get from the API only the info that's needed.

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:

channelResult.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/.

For an almost complete implementation of the API calls I described above (excluding the loop that fills up the list videoList), I'll recommend to make use of the following sample program from Google: MyUploads.java. The loop that constructs the list videoList is similar to the one found in GeolocationSearch.java. The implementation of method Auth.authorize is to be found in Auth.java.

Upvotes: 2

Related Questions