为特定的 YouTube 订阅生成视频上传列表

Posted

技术标签:

【中文标题】为特定的 YouTube 订阅生成视频上传列表【英文标题】:Generating list of video uploads for a particular YouTube subscription 【发布时间】:2021-01-16 09:56:55 【问题描述】:

我正在尝试从我订阅的频道生成视频列表。

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);
    

我已阅读YouTube API to fetch all videos on a channel,但我的问题有点不同,因为我试图从Subscription 而不是Channel 获取视频列表。

我尝试隔离频道 ID,以便按照 video from Google Developers 中有关如何执行此操作的说明进行操作。我用String channelId = response.getItems().get(0).getSnippet().getResourceId().getChannelId();

但是,当我运行上面的代码时,应该以 JSON 表示法从该频道打印出一个视频列表,我看到了这个错误:

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)

显然,我没有正确隔离频道 ID,否则我误解了视频。从 YouTube 订阅中获取视频的正确方法是什么?

编辑

我的主要方法中的以下更改有效:

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);

【问题讨论】:

请注意,当调用Subscriptions.list API 端点并将参数mine 设置为true 时,您将返回(引用):经过身份验证的用户订阅的提要。 这将是一个包含 Subscription resources 的结果集,每个代表一个频道(即 YouTube 用户)订阅了您的频道 @stvar 你比我知道的更多,但我不相信这是正确的。我独立测试了该方法,它似乎生成了我订阅的频道。 确实你是对的!这与我上面的评论所说的相反。 (我将保留该评论。) 现在我明白你的意思了。 问题在于.setPlaylistId(channelId),因为频道 ID 与播放列表 ID 属于不同的类别。但是如果想列出给定频道上传的所有视频,我推荐reading my answer to this very issue。 【参考方案1】:

上面代码的问题归结为以下文档条目:

playlistId(字符串)

playlistId 参数指定要检索其播放列表项的播放列表的唯一 ID。请注意,尽管这是一个可选参数,但每个检索播放列表项的请求都必须为 id 参数或 playlistId 参数指定一个值。

现在应该明白了:playlistId 是播放列表的 ID,因此不能是频道的 ID。

但要列出给定频道的所有上传视频(由其 ID 标识),必须执行以下操作:

调用PlaylistItems.list API 端点,将参数playlistId 设置为该频道的上传播放列表的ID。

通过调用Channels.list 端点并设置参数id 设置为您的频道ID,可以很容易地获得后一个ID。

然后将在端点的 JSON 响应中作为属性值找到上传播放列表 ID:

items[0].contentDetails.relatedPlaylists.uploads.

翻译成Java,这个属性路径会变成下面的getter链,以getUploads结尾:

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

请注意,对于给定的频道,您只需获取一次上传播放列表 ID,然后就可以随意使用它。

通常,频道 ID 及其对应的上传播放列表 ID 通过s/^UC([0-9a-zA-Z_-]22)$/UU\1/ 关联。

【讨论】:

以上是关于为特定的 YouTube 订阅生成视频上传列表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 youtube-dl 从播放列表中的 Youtube 视频中提取上传日期、标题、URL 和持续时间?

使用 YouTube API 列出从新西兰上传的纪录片视频

nodejs爬虫笔记

YouTube API频道的视频列表

如何显示 youtube 视频缩略图

YouTube API 频道的视频列表