.netyoutubeyoutube-apiyoutube-data-apicaptions

How to add captions to youtube video with YoutubeApi v3 in .Net


I am having issues adding captions to videos uploaded to my company's youtube channels. I want to do it with Google's Youtube Api v3 in .Net. I am able to successfully upload videos to the channels, but I get the following error when trying to send captions: "System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden)."

As far as I can tell, my credentials do not forbid me from uploading captions. I am able to upload videos without any problem.

I created the refresh token using the instructions here: Youtube API single-user scenario with OAuth (uploading videos)

This is the code I'm using to create the YouTubeService object:

    private YouTubeService GetYouTubeService()
    {
        string clientId = "clientId";
        string clientSecret = "clientSecret";
        string refreshToken = "refreshToken";
        try
        {
            ClientSecrets secrets = new ClientSecrets()
            {
                ClientId = clientId,
                ClientSecret = clientSecret
            };

            var token = new TokenResponse { RefreshToken = refreshToken };
            var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
                new GoogleAuthorizationCodeFlow.Initializer
                {
                    ClientSecrets = secrets,
                    Scopes = new[] { YouTubeService.Scope.Youtube, YouTubeService.Scope.YoutubeUpload, YouTubeService.Scope.YoutubeForceSsl }
                }),
                "user",
                token);

            var service = new YouTubeService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credentials,
                ApplicationName = "TestProject"
            });

            service.HttpClient.Timeout = TimeSpan.FromSeconds(360);
            return service;
        }
        catch (Exception ex)
        {
            Log.Error("YouTube.GetYouTubeService() => Could not get youtube service. Ex: " + ex);
            return null;
        }

    }

And here is the code I have for uploading the caption file:

    private void UploadCaptionFile(String videoId)
    {
        try
        {
            Caption caption = new Caption();
            caption.Snippet = new CaptionSnippet();
            caption.Snippet.Name = videoId + "_Caption";
            caption.Snippet.Language = "en";
            caption.Snippet.VideoId = videoId;
            caption.Snippet.IsDraft = false;

            WebRequest req = WebRequest.Create(_urlCaptionPath);
            using (Stream stream = req.GetResponse().GetResponseStream())
            {
                CaptionsResource.InsertMediaUpload captionInsertRequest = _youtubeService.Captions.Insert(caption, "snippet", stream, "*/*");
                captionInsertRequest.Sync = true;
                captionInsertRequest.ProgressChanged += captionInsertRequest_ProgressChanged;
                captionInsertRequest.ResponseReceived += captionInsertRequest_ResponseReceived;

                IUploadProgress result = captionInsertRequest.Upload();
            }
        }
        catch (Exception ex)
        {
            Log.Error("YouTube.UploadCaptionFile() => Unable to upload caption file. Ex: " + ex);
        }
    }

    void captionInsertRequest_ResponseReceived(Caption obj)
    {
        Log.Info("YouTube.captionInsertRequest_ResponseReceived() => Caption ID " + obj.Id + " was successfully uploaded for this clip.");
        Utility.UpdateClip(_videoClip);
    }

    void captionInsertRequest_ProgressChanged(IUploadProgress obj)
    {
        switch (obj.Status)
        {
            case UploadStatus.Uploading:
                Console.WriteLine("{0} bytes sent.", obj.BytesSent);
                break;

            case UploadStatus.Failed:
                Log.Error("YouTube.UploadCaptionFile() => An error prevented the upload from completing. " + obj.Exception);
                break;
        }
    }

I have spent the past couple days working on this issue, and I have been unable to make any progress. The YouTubeApi v3 doesn't have much information on adding captions. All I was able to find was some old information for v2, which required some POST calls that require keys that I don't have. I would like to be able to add the captions using the API's build-in methods for doing so.

If anyone has experience dealing with this issue, I would greatly appreciate any help you could offer.

EDIT:

Here is my code for sending the videos to YouTube.

    public string UploadClipToYouTube()
    {
        try
        {
            var video = new Google.Apis.YouTube.v3.Data.Video();
            video.Snippet = new VideoSnippet();
            video.Snippet.Title = _videoClip.Name;
            video.Snippet.Description = _videoClip.Description;
            video.Snippet.Tags = GenerateTags();
            video.Snippet.DefaultAudioLanguage = "en";
            video.Snippet.DefaultLanguage = "en";
            video.Snippet.CategoryId = "22";
            video.Status = new VideoStatus();
            video.Status.PrivacyStatus = "unlisted";

            WebRequest req = WebRequest.Create(_videoPath);
            using (Stream stream = req.GetResponse().GetResponseStream())
            {
                VideosResource.InsertMediaUpload insertRequest = _youtubeService.Videos.Insert(video, "snippet, status", stream, "video/*");
                insertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
                insertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;

                insertRequest.Upload();
            }
            return UploadedVideoId;
        }
        catch (Exception ex)
        {
            Log.Error("YouTube.UploadClipToYoutube() => Error attempting to authenticate for YouTube. Ex: " + ex);
            return "";
        }
    }

    void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
    {
        switch (progress.Status)
        {
            case UploadStatus.Uploading:
                Log.Info("YouTube.videosInsertRequest_ProgressChanged() => Uploading to Youtube. " + progress.BytesSent + " bytes sent.");
                break;

            case UploadStatus.Failed:
                Log.Error("YouTube.videosInsertRequest_ProgressChanged() => An error prevented the upload from completing. Exception: " + progress.Exception);
                break;
        }
    }

    void videosInsertRequest_ResponseReceived(Google.Apis.YouTube.v3.Data.Video video)
    {
        Log.Info("YouTube.videosInsertRequest_ResponseReceived() => Video was successfully uploaded to YouTube.  The YouTube id is " + video.Id);
        UploadedVideoId = video.Id;
        UploadCaptionFile(video.Id);
    }

Solution

  • Alright, I whipped up something really quick for you (honestly about 15 minutes) that I translated from Java. First there are more than a few things I have noticed with your current code, but this is not CodeReview.

    The YouTubeApi v3 doesn't have much information on adding captions. All I was able to find was some old information for v2

    You are correct about this, it doesn't at this time (v3), but hopefully sometime in the near future. This doesn't stop us from modifying v2 though to our advantage as the api are very similar...

    Code Tried & Tested

    private async Task addVideoCaption(string videoID) //pass your video id here..
            {
                UserCredential credential;
                //you should go out and get a json file that keeps your information... You can get that from the developers console...
                using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
                {
                    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                        GoogleClientSecrets.Load(stream).Secrets,
                        new[] { YouTubeService.Scope.YoutubeForceSsl, YouTubeService.Scope.Youtube, YouTubeService.Scope.Youtubepartner },
                        "ACCOUNT NAME HERE",
                        CancellationToken.None,
                        new FileDataStore(this.GetType().ToString())
                    );
                }
                //creates the service...
                var youtubeService = new Google.Apis.YouTube.v3.YouTubeService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = this.GetType().ToString(),
                });
    
                //create a CaptionSnippet object...
                CaptionSnippet capSnippet = new CaptionSnippet();
                capSnippet.Language = "en";
                capSnippet.Name = videoID + "_Caption";
                capSnippet.VideoId = videoID;
                capSnippet.IsDraft = false;
    
                //create new caption object
                Caption caption = new Caption();       
    
                //set the completed snippet to the object now...
                caption.Snippet = capSnippet;
    
                //here we read our .srt which contains our subtitles/captions...
                using (var fileStream = new FileStream("filepathhere", FileMode.Open))
                {
                    //create the request now and insert our params...
                    var captionRequest = youtubeService.Captions.Insert(caption, "snippet",fileStream,"application/atom+xml");
    
                    //finally upload the request... and wait.
                    await captionRequest.UploadAsync();
                }
    
            }
    

    Here's an example .srt file if you do not know what one look's like or how it's formatted.

    1
    00:00:00,599 --> 00:00:03,160
    >> Caption Test zaggler@ StackOverflow
    
    2
    00:00:03,160 --> 00:00:05,770
    >> If you're reading this it worked!
    

    YouTube Video Proof. It's a short clip about 5 seconds just to show you that it work's and what it look's like. AND... No the video isn't mine, grabbed it for testing ONLY :)

    Good Luck and Happy Programming!