如何通过 YouTube 数据 API v3 从我自己的 YouTube 频道获取所有视频
How to get all videos from my own YouTube channel via YouTube Data API v3
我需要从我自己的 YouTube 频道获取所有视频的 ID 和标题。有些视频未公开,但我也想获取它们。我使用这个版本的客户端库:
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-youtube</artifactId>
<version>v3-rev222-1.25.0</version>
</dependency>
根据文档,我使用这个 Java 代码:
YouTube.Search.List request = youtubeService.search()
.list("id,snippet");
SearchListResponse response = request.setChannelId(channelId)
.setForMine(true)
.setMaxResults(50L)
.setType("video")
.execute();
但是遇到了这个异常:
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."
}
此外,我使用此页面上的交互式工具遇到了同样的错误:
https://developers.google.com/youtube/v3/docs/search/list?apix=true
.
如果我删除 .setForMine(true)
它工作正常,但不会给我 未列出的 视频(只给我 public 视频)。
是否有可能通过 API 从我自己的频道中获取所有视频(包括 未列出)的 ID 和标题?
对您的问题的简短回答是:的确,有一个 API 将提供您频道的 所有 视频元数据,包括 [=80] 的元数据=]未列出 个视频。
较长的回答,请耐心等待:
首先,请注意给定的视频具有以下几种隐私状态:
status.privacyStatus
(string)
The video's privacy status.
Valid values for this property are:
- private
- public
- unlisted
要获取您频道上传的所有视频的 ID,无论其隐私状态如何,您都必须调用 PlaylistItems.list
API endpoint queried with the parameter playlistId
设置为您频道上传播放列表的 ID,同时发出 OAuth 授权请求(即:向 API 传递一个有效的访问令牌;仅使用 API 密钥将使端点仅 return public 个视频):
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);
现在,Videos.list
API 端点将为您提供由 videoIdList
收集 ID 的视频的所有元数据。
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();
}
请注意,如果 videoIdList
在上面的循环开始时大小为 N
-- 因为 Videos.list
端点的参数 id
可以指定为以逗号分隔的视频 ID 列表 --,循环代码通过适当使用 id
的功能将对 Videos.list
端点的调用次数从 N
减少到 Math.floor(N / 50) + (N % 50 ? 1 : 0)
提到。
上传播放列表 ID -- uploadsPlaylistId
-- 可以很容易地通过调用设置为 true
:
的 Channels.list
endpoint queried with the parameter id
set to your channel's ID, or, otherwise, queried with the parameter mine
来获得
YouTube.Channels.List channelRequest =
youtube.channels().list("contentDetails");
channelRequest.setMine(true);
channelRequest.setFields("items/contentDetails/relatedPlaylists/uploads");
ChannelListResponse channelResult = channelRequest.execute();
请注意,上面我使用 fields
参数只从 API 获取所需的信息。
然后可以在端点的 JSON 响应中找到上传播放列表 ID 作为 属性:
的值
items[0].contentDetails.relatedPlaylists.uploads
.
转换为 Java,此 属性 路径将成为以下 getter 链,以 getUploads
:
结尾
channelResult.getItems().get(0).getContentDetails().getRelatedPlaylists().getUploads()
.
请注意,对于给定频道,您只需获取一次上传播放列表 ID,然后可以根据需要多次使用。通常,频道 ID 及其对应的上传播放列表 ID 通过 s/^UC([0-9a-zA-Z_-]{22})$/UU/
.
关联
为了几乎完整地实现我上面描述的 API 调用(不包括填充列表 videoList
的循环),我建议使用以下示例程序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
.
我需要从我自己的 YouTube 频道获取所有视频的 ID 和标题。有些视频未公开,但我也想获取它们。我使用这个版本的客户端库:
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-youtube</artifactId>
<version>v3-rev222-1.25.0</version>
</dependency>
根据文档,我使用这个 Java 代码:
YouTube.Search.List request = youtubeService.search()
.list("id,snippet");
SearchListResponse response = request.setChannelId(channelId)
.setForMine(true)
.setMaxResults(50L)
.setType("video")
.execute();
但是遇到了这个异常:
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."
}
此外,我使用此页面上的交互式工具遇到了同样的错误:
https://developers.google.com/youtube/v3/docs/search/list?apix=true
.
如果我删除 .setForMine(true)
它工作正常,但不会给我 未列出的 视频(只给我 public 视频)。
是否有可能通过 API 从我自己的频道中获取所有视频(包括 未列出)的 ID 和标题?
对您的问题的简短回答是:的确,有一个 API 将提供您频道的 所有 视频元数据,包括 [=80] 的元数据=]未列出 个视频。
较长的回答,请耐心等待:
首先,请注意给定的视频具有以下几种隐私状态:
status.privacyStatus
(string)
The video's privacy status. Valid values for this property are:
- private
- public
- unlisted
要获取您频道上传的所有视频的 ID,无论其隐私状态如何,您都必须调用 PlaylistItems.list
API endpoint queried with the parameter playlistId
设置为您频道上传播放列表的 ID,同时发出 OAuth 授权请求(即:向 API 传递一个有效的访问令牌;仅使用 API 密钥将使端点仅 return public 个视频):
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);
现在,Videos.list
API 端点将为您提供由 videoIdList
收集 ID 的视频的所有元数据。
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();
}
请注意,如果 videoIdList
在上面的循环开始时大小为 N
-- 因为 Videos.list
端点的参数 id
可以指定为以逗号分隔的视频 ID 列表 --,循环代码通过适当使用 id
的功能将对 Videos.list
端点的调用次数从 N
减少到 Math.floor(N / 50) + (N % 50 ? 1 : 0)
提到。
上传播放列表 ID -- uploadsPlaylistId
-- 可以很容易地通过调用设置为 true
:
Channels.list
endpoint queried with the parameter id
set to your channel's ID, or, otherwise, queried with the parameter mine
来获得
YouTube.Channels.List channelRequest =
youtube.channels().list("contentDetails");
channelRequest.setMine(true);
channelRequest.setFields("items/contentDetails/relatedPlaylists/uploads");
ChannelListResponse channelResult = channelRequest.execute();
请注意,上面我使用 fields
参数只从 API 获取所需的信息。
然后可以在端点的 JSON 响应中找到上传播放列表 ID 作为 属性:
的值items[0].contentDetails.relatedPlaylists.uploads
.
转换为 Java,此 属性 路径将成为以下 getter 链,以 getUploads
:
channelResult.getItems().get(0).getContentDetails().getRelatedPlaylists().getUploads()
.
请注意,对于给定频道,您只需获取一次上传播放列表 ID,然后可以根据需要多次使用。通常,频道 ID 及其对应的上传播放列表 ID 通过 s/^UC([0-9a-zA-Z_-]{22})$/UU/
.
为了几乎完整地实现我上面描述的 API 调用(不包括填充列表 videoList
的循环),我建议使用以下示例程序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
.