对接YouTube平台实现上传视频——Java实现

对接YouTube平台实现上传视频

背景描述

前段时间公司要求对接海外YouTube平台实现视频上传的功能,由于海外文档描述不详细,且本人英语功底不好,过程中踩了很多坑,写出这篇文章出来希望能帮助到有需要的人。

对接YouTube平台的准备

开发环境:idea 、jdk1.8、maven

开发准备

  1. 需要一个Google账号
  2. 需要登录到Google控制台创建应用开启YouTube平台Api
  3. 需要需要去Google控制台下载client_secrets.json文件

下面是图文教程:

  1. Google控制台 创建应用
    创建应用1.1 选择应用类型
    在这里插入图片描述1.2 client_secrets.json文件
    在这里插入图片描述2.开启YouTube的Api
    在这里插入图片描述2.1 启用此api
    在这里插入图片描述环境准备好后咱们先了解一下接入流程

接入流程

调用YouTube Api需要用到Google OAuth2.0 授权,授权完成之后Google会返回给你一个访问令牌,在令牌有效期内可以访问Google Api 而 YouTube Api就是Google Api 下的一种。
下面来看下Google OAuth2.0的授权流程:

  1. 应用向Google oaAuth2服务器发起对用户对该应用的授权请求,利用Google Api返回给应用一个用于用户给应用授权的链接
  2. 用户在浏览器打开授权链接后选择授权给应用的权限后点击确定,Google oaAuth2会回调该应用配置在谷歌后台的回调地址
  3. Google oaAuth服务器回调应用接口会返回用户授权码(code)以及用户给应用赋予的权限(scope),应用拿到用户授权码之后向GoogleoaAuth服务器发起请求把用户授权码兑换成令牌
  4. 拿到oaAuth服务器返回的令牌之后就能够访问YouTube的API了,这里要注意Google oaAuth2服务器返回的令牌是有时间限制的在谷歌后台可以设置过期时间,当令牌过期后需要拿着Google oaAuth2令牌里面的refresh_token向Google oaAuth2服务器重新申请令牌

在这里插入图片描述了解完流程之后咱们可以正式进入开发了

代码开发

  1. maven坐标
<!-- maven 坐标 -->
        <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-youtube</artifactId>
            <version>v3-rev222-1.25.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.api-client</groupId>
            <artifactId>google-api-client-gson</artifactId>
            <version>2.0.0</version>
        </dependency>
  1. 配置类
// 基础配置
@Configuration
public class YoutubeConfig {
    
    
		// 这个是client_secrets.json的文件路径,这是自己写的demo,只要能加载就行
    private static String clientSecretsPath = "E:\\Java_source_code\\youtube-video\\youtube-admin\\src\\main\\resources\\config\\client_secrets.json";
		// 调用YouTubeApi所需权限列表,具体哪个url对应那个权限请翻阅YouTube官方文档
    private static List<String> roleList = Arrays.asList(
            "https://www.googleapis.com/auth/youtube",
            "https://www.googleapis.com/auth/youtube.channel-memberships.creator",
            "https://www.googleapis.com/auth/youtube.force-ssl",
            "https://www.googleapis.com/auth/youtube.readonly",
            "https://www.googleapis.com/auth/youtube.upload",
            "https://www.googleapis.com/auth/youtubepartner",
            "https://www.googleapis.com/auth/youtubepartner-channel-audit",
            "https://www.googleapis.com/auth/youtubepartner-content-owner-readonly"
    );

    @Bean
    public HttpTransport getHttpTransport() throws GeneralSecurityException, IOException {
    
    
        return GoogleNetHttpTransport.newTrustedTransport();
    }

    @Bean
    public JsonFactory getJsonFactory() {
    
    
        return GsonFactory.getDefaultInstance();
    }

    @Bean
    public GoogleClientSecrets getGoogleClientSecrets(JsonFactory jsonFactory) throws IOException {
    
    
        InputStream stream = new FileInputStream(new File(clientSecretsPath));
        return GoogleClientSecrets.load(jsonFactory, new InputStreamReader(stream));
    }


    @Bean
    public GoogleAuthorizationCodeFlow getGoogleAuthorizationCodeFlow(HttpTransport httpTransport,
                                                                      JsonFactory jsonFactory,
                                                                      GoogleClientSecrets clientSecrets) {
    
    
        return new GoogleAuthorizationCodeFlow.Builder(
                httpTransport, jsonFactory, clientSecrets,
                roleList)
                .build();
    }

    @Bean
    public RestTemplate getRestTemplate() {
    
    
        return new RestTemplate();
    }

}

  1. 获取Google授权
@Controller
@RequestMapping(value = "/youtube")
public class YoutubeAuthController {
    
    

    @Value("${youtube.redirect-uri}")
    private String redirectUri;

    @Autowired
    private GoogleAuthorizationCodeFlow flow;

    @Autowired
    private GoogleClientSecrets clientSecrets;

    private static GoogleTokenResponse googleTokenResponse;


    private static String DOT = ",";

    public static ConcurrentHashMap<String,Credential> CREDENTIAL_MAP = new ConcurrentHashMap<>();


    /**
     * Google回调接口, 只有用户第一次授权时会返回refreshToken, 这里status作为requestId使用
     * @param code
     * @param scope
     * @param state
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/callback")
    @ResponseBody
    public String getYoutubeCallback(String code, String scope,String state) throws Exception {
    
    
        List<NameValuePair> parameters = new ArrayList<>();
        parameters.add(new BasicNameValuePair("code", code));
        parameters.add(new BasicNameValuePair("client_id", clientSecrets.getWeb().getClientId()));
        parameters.add(new BasicNameValuePair("client_secret", clientSecrets.getWeb().getClientSecret()));
        parameters.add(new BasicNameValuePair("grant_type", "authorization_code"));
        parameters.add(new BasicNameValuePair("redirect_uri",redirectUri));
        UrlEncodedFormEntity encodedFormEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8.name());

        String url = "https://oauth2.googleapis.com/token";
        HttpPost request = new HttpPost();
        request.setEntity(encodedFormEntity);
        String response = HttpClientUtil.doPost(url, encodedFormEntity);
        System.out.println(response);
        TokenResponse tokenResponse = JSON.parseObject(response,TokenResponse.class);
        Credential credential = flow.createAndStoreCredential(tokenResponse, state);

        CREDENTIAL_MAP.put(state,credential);
        return "ok";
    }

    private String getRoleString(String roles) {
    
    
        if (roles.contains(DOT) && roles.endsWith(DOT)) {
    
    
            return roles.substring(0,roles.length() - 1);
        } else {
    
    
            return roles;
        }
    }

    /**
     * Google刷新令牌接口
     * @return
     */
    @RequestMapping(value = "/refreshToken")
    @ResponseBody
    public String refreshToken(String uid) throws Exception {
    
    
        Credential credential = CREDENTIAL_MAP.get(uid);
        List<NameValuePair> parameters = new ArrayList<>();
        parameters.add(new BasicNameValuePair("client_id", clientSecrets.getWeb().getClientId()));
        parameters.add(new BasicNameValuePair("client_secret", clientSecrets.getWeb().getClientSecret()));
        parameters.add(new BasicNameValuePair("grant_type", "refresh_token"));
        parameters.add(new BasicNameValuePair("refresh_token",credential.getRefreshToken()));
        UrlEncodedFormEntity encodedFormEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8.name());

        String url = "https://oauth2.googleapis.com/token";
        HttpPost request = new HttpPost();
        request.setEntity(encodedFormEntity);
        return HttpClientUtil.doPost(url, encodedFormEntity);
    }

    /**
     * 获取用户授权url接口
     * @param uid
     * @return
     */
    @GetMapping(value = "/applyRole")
    @ResponseBody
    public String toApplyRole(String uid) {
    
    
        try {
    
    
            return flow.newAuthorizationUrl()
                    .setRedirectUri(redirectUri)
                    .setAccessType("offline")
                    // 这个值可以不设置,授权成功后Google会返回这个值给你
                    .setState(uid)
                    // 这个值是指定账号的,不用指定账号不用设置
                    .set("login_hint",uid)
                    .build();
        } catch (Exception e) {
    
    

            e.printStackTrace();
            return "the request happen a error in runtime";
        }
    }

    /**
     * Google撤销令牌接口
     * @return
     */
    @RequestMapping(value = "/revoke")
    @ResponseBody
    public String revokeToken(String refresh_token) throws Exception {
    
    
        Credential credential = CREDENTIAL_MAP.get(uid);

        String url1 = "https://oauth2.googleapis.com/revoke?token=" + refresh_token;
   //     String url2 = "https://oauth2.googleapis.com/revoke?token="+"1//0evHFaeshE0tACgYIARAAGA4SNwF-L9IrQoRaXWvYVLqgGk8jOl_KWlobz8q_uqk36vuwWD6MVHKoBHr-7n7e5aLNXP0AYYh5xQ0";
        return HttpClientUtil.doPost(url1, null);
    }
}

记住:同一个账号只能给一个应用授权一次,同一个账号再次授权会报错,这段代码先理解一下,了解一下我这是啥意思,再动手写自己的代码,不要盲目copy !!!

拿到访问令牌之后就能对YouYube的api进行访问了
下面试操作YouTube视频接口的代码

@Controller
@RequestMapping(value = "/api")
public class YouTubeApiController {
    
    

    @Autowired
    private JsonFactory jsonFactory;

    @Autowired
    private HttpTransport httpTransport;

    @Autowired
    private GoogleAuthorizationCodeFlow flow;

    private static final String APP_NAME = "web-app-youtube";





    @GetMapping(value = "/getChannel")
    @ResponseBody
    public ChannelListResponse getChannel(String uid) throws IOException {
    
    
         Credential credential = YoutubeAuthController.CREDENTIAL_MAP.get(uid);
        // 获取凭证
     /*   GoogleTokenResponse tokenResponse = JSON.parseObject("{\n" +
                "  \"access_token\": \"ya29.a0AX9GBdWDTl_WDHjAhMhqfHOv8Tee5o3Y8G-Un46rjLxVIXMBaUC8aUHzQWtwCzENq9EBPkN5WoXgiIgReOmBhEjIzJgZ3ZAbQt0em3m5NNFQe6LtL3Z0oj6UMHYHd0H4UJ2_N5Td1g3ggudW_539A09HtTV2aCgYKAdcSARASFQHUCsbCk4dDMR9R3-VivIgsglOayw0163\",\n" +
                "  \"expires_in\": 3599,\n" +
                "  \"refresh_token\": \"1//0eggCEbOSfmnnCgYIARAAGA4SNwF-L9IryVDE5t8GTDyOVAzPOPFc-TGJmiZOkUkzTVPLZuYMNOURZYA2fklO1_nhlSy3SOAsU4w\",\n" +
                "  \"scope\": \"https://www.googleapis.com/auth/youtubepartner https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtubepartner-channel-audit https://www.googleapis.com/auth/youtube.readonly https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner-content-owner-readonly https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/youtube.channel-memberships.creator\",\n" +
                "  \"token_type\": \"Bearer\"\n" +
                "}",GoogleTokenResponse.class);*/
        //Credential credential = flow.createAndStoreCredential(tokenResponse.transfer(),null);


        YouTube youtubeService = new YouTube.Builder(httpTransport, jsonFactory, credential).build();

        String respParam = "brandingSettings,id,status";
        YouTube.Channels.List request = youtubeService.channels().list(respParam);
        ChannelListResponse response = request
                .setMine(true)
                .execute();
        return response;
    }



    /**
     * Google上传视频接口
     * @return
     */
    @RequestMapping(value = "/uploadVideo")
    @ResponseBody
    public Video uploadVideo(String uid, MultipartFile file) throws Exception {
    
    

      /*  // 获取凭证
        GoogleTokenResponse tokenResponse = JSON.parseObject("{\n" +
                "  \"access_token\": \"ya29.a0AX9GBdWDTl_WDHjAhMhqfHOv8Tee5o3Y8G-Un46rjLxVIXMBaUC8aUHzQWtwCzENq9EBPkN5WoXgiIgReOmBhEjIzJgZ3ZAbQt0em3m5NNFQe6LtL3Z0oj6UMHYHd0H4UJ2_N5Td1g3ggudW_539A09HtTV2aCgYKAdcSARASFQHUCsbCk4dDMR9R3-VivIgsglOayw0163\",\n" +
                "  \"expires_in\": 3599,\n" +
                "  \"refresh_token\": \"1//0eggCEbOSfmnnCgYIARAAGA4SNwF-L9IryVDE5t8GTDyOVAzPOPFc-TGJmiZOkUkzTVPLZuYMNOURZYA2fklO1_nhlSy3SOAsU4w\",\n" +
                "  \"scope\": \"https://www.googleapis.com/auth/youtubepartner https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtubepartner-channel-audit https://www.googleapis.com/auth/youtube.readonly https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner-content-owner-readonly https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/youtube.channel-memberships.creator\",\n" +
                "  \"token_type\": \"Bearer\"\n" +
                "}",GoogleTokenResponse.class);

        Credential credential = flow.createAndStoreCredential(tokenResponse.transfer(),uid);*/

        Credential credential = YoutubeAuthController.CREDENTIAL_MAP.get(uid);
        YouTube youtubeService =  new YouTube.Builder(httpTransport, jsonFactory, credential).build();

        Video uploadedVideo = new Video();
        VideoStatus status = new VideoStatus();
        status.setPrivacyStatus("public");
        uploadedVideo.setStatus(status);
        VideoSnippet snippet = new VideoSnippet();
        snippet.setTitle(file.getOriginalFilename());
        uploadedVideo.setSnippet(snippet);
        InputStreamContent mediaContent =
                new InputStreamContent("application/octet-stream",
                        new BufferedInputStream(file.getInputStream()));

        YouTube.Videos.Insert videoInsert = youtubeService.videos()
                .insert("snippet,status,id,player", uploadedVideo, mediaContent);
        MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();
        uploader.setDirectUploadEnabled(false);
        MediaHttpUploaderProgressListener progressListener = e -> {
    
    
            switch (e.getUploadState()) {
    
    
                case INITIATION_STARTED:
                    System.out.println("Initiation Started");
                    break;
                case INITIATION_COMPLETE:
                    System.out.println("Initiation Completed");
                    break;
                case MEDIA_IN_PROGRESS:
                    System.out.println("Upload in progress");
                    System.out.println("Upload percentage: " + e.getProgress());
                    break;
                case MEDIA_COMPLETE:
                    System.out.println("Upload Completed!");
                    break;
                case NOT_STARTED:
                    System.out.println("Upload Not Started!");
                    break;
            }
        };
        uploader.setProgressListener(progressListener);
        return videoInsert.execute();
    }

    /**
     * 获取视频列表
     * @param uid
     * @return
     * @throws IOException
     */
    @GetMapping(value = "/videoList")
    @ResponseBody
    public String getVideoList(String uid) throws IOException {
    
    
        // 获取凭证
    /*    TokenResponse tokenResponse = JSON.parseObject("{\n" +
                "  \"access_token\": \"ya29.a0AX9GBdWDTl_WDHjAhMhqfHOv8Tee5o3Y8G-Un46rjLxVIXMBaUC8aUHzQWtwCzENq9EBPkN5WoXgiIgReOmBhEjIzJgZ3ZAbQt0em3m5NNFQe6LtL3Z0oj6UMHYHd0H4UJ2_N5Td1g3ggudW_539A09HtTV2aCgYKAdcSARASFQHUCsbCk4dDMR9R3-VivIgsglOayw0163\",\n" +
                "  \"expires_in\": 3599,\n" +
                "  \"refresh_token\": \"1//0eggCEbOSfmnnCgYIARAAGA4SNwF-L9IryVDE5t8GTDyOVAzPOPFc-TGJmiZOkUkzTVPLZuYMNOURZYA2fklO1_nhlSy3SOAsU4w\",\n" +
                "  \"scope\": \"https://www.googleapis.com/auth/youtubepartner https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtubepartner-channel-audit https://www.googleapis.com/auth/youtube.readonly https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner-content-owner-readonly https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/youtube.channel-memberships.creator\",\n" +
                "  \"token_type\": \"Bearer\"\n" +
                "}",TokenResponse.class);
        Credential credential = flow.createAndStoreCredential(tokenResponse,uid);
        */
         Credential credential = YoutubeAuthController.CREDENTIAL_MAP.get(uid);
        YouTube youtubeService =  new YouTube.Builder(httpTransport, jsonFactory, credential).build();
        String part = "contentDetails,fileDetails,id,liveStreamingDetails,localizations,player,processingDetails,recordingDetails,snippet,statistics,status,suggestions,topicDetails";
        String videoId = "sYljcKUToF0";
        VideoListResponse listResp = youtubeService.videos().list(part).setId(videoId).execute();
        System.out.println(JSON.toJSONString(listResp));
        return JSON.toJSONString(listResp);
    }
}

请理解上面这段代码,以上便是访问YouTube的Api全部内容了,注意Google应用对接口的访问有配额限制,限制每日配额是1w , 其中 查询消耗配额为 1,新增修改删除消耗配额为50,上传视频消耗1600配额,超过配额接口将不可访问,需要申请提高配额,
另外上传的视频是有格式限制的:

YouTube支持的视频格式:

.MOV .MP4 .3GPP .MPEGPS
.MPEG-1 .MPG .WebM .FLV
.MPEG-2 .AVI .DNxHR .ProRes
.MPEG4 .WMV .CineForm .HEVC (h265)

猜你喜欢

转载自blog.csdn.net/weixin_44809686/article/details/128732733