15

I have created console application using C#. Which will upload Video from local drive to youtube. I have created new app in google api using this link. I have also installed all required packages using nuget. When I run my application I am getting error as "Access Denied" I am not able to find the issue.

I am getting error in Task Run() method.

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;

namespace Google.Apis.YouTube.Samples
{
  /// <summary>
  /// YouTube Data API v3 sample: create a playlist.
  /// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
  /// See https://developers.google.com/api-client-library/dotnet/get_started
  /// </summary>
  internal class PlaylistUpdates
  {
    [STAThread]
    static void Main(string[] args)
    {
      Console.WriteLine("YouTube Data API: Playlist Updates");
      Console.WriteLine("==================================");

      try
      {
        new PlaylistUpdates().Run().Wait();
      }
      catch (AggregateException ex)
      {
        foreach (var e in ex.InnerExceptions)
        {
          Console.WriteLine("Error: " + e.Message);
        }
      }

      Console.WriteLine("Press any key to continue...");
      Console.ReadKey();
    }

    private async Task Run()
    {
      UserCredential credential;
      using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
      {
        credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
            GoogleClientSecrets.Load(stream).Secrets,
            // This OAuth 2.0 access scope allows for full read/write access to the
            // authenticated user's account.
            new[] { YouTubeService.Scope.Youtube },
            "user",
            CancellationToken.None,
            new FileDataStore(this.GetType().ToString())
        );
      }

      var youtubeService = new YouTubeService(new BaseClientService.Initializer()
      {
        HttpClientInitializer = credential,
        ApplicationName = this.GetType().ToString()
      });

      // Create a new, private playlist in the authorized user's channel.
      var newPlaylist = new Playlist();
      newPlaylist.Snippet = new PlaylistSnippet();
      newPlaylist.Snippet.Title = "Test Playlist";
      newPlaylist.Snippet.Description = "A playlist created with the YouTube API v3";
      newPlaylist.Status = new PlaylistStatus();
      newPlaylist.Status.PrivacyStatus = "public";
      newPlaylist = await youtubeService.Playlists.Insert(newPlaylist, "snippet,status").ExecuteAsync();

      // Add a video to the newly created playlist.
      var newPlaylistItem = new PlaylistItem();
      newPlaylistItem.Snippet = new PlaylistItemSnippet();
      newPlaylistItem.Snippet.PlaylistId = newPlaylist.Id;
      newPlaylistItem.Snippet.ResourceId = new ResourceId();
      newPlaylistItem.Snippet.ResourceId.Kind = "youtube#video";
      newPlaylistItem.Snippet.ResourceId.VideoId = "GNRMeaz6QRI";
      newPlaylistItem = await youtubeService.PlaylistItems.Insert(newPlaylistItem, "snippet").ExecuteAsync();

      Console.WriteLine("Playlist item id {0} was added to playlist id {1}.", newPlaylistItem.Id, newPlaylist.Id);
    }
  }
}

Do I need to pass 'user' parameter the gmail username?

Any working example using C# (console/web) ?

Help Appreciated.

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
SHEKHAR SHETE
  • 5,964
  • 15
  • 85
  • 143
  • @Chad so what's a problem exactly? Where is fails? Same "Access Denied"? – Evk Mar 20 '18 at 13:26
  • I get `Inner Exception 1: TokenResponseException: Error:"invalid_client", Description:"Unauthorized", Uri:""` as an error, see my post here https://stackoverflow.com/questions/49364545/net-youtube-google-apis-upload-credential-error I see the same code example and questions over and over with no answers. I wonder if the .NET library even works. I have been at it for over a week now. – Chad Mar 20 '18 at 13:32
  • @Chad I just tried it and works fine for me (not everything, just authorization part). So that's why I'm asking where exactly you have error. On which line? Authorization or later? – Evk Mar 20 '18 at 13:42
  • in `Task Run()` my code executes to the line/method `credential = await GoogleWebAuthorizationBroker.AuthorizeAsync()` – Chad Mar 20 '18 at 13:45
  • @Chad your error suggests you have some problems with client id\secret. Try initialize them directly: `AuthorizeAsync(new ClientSecrets {ClientId = "your id", ClientSecret = "secret"}...)`. Use OAuth2.0 credentials from this page: https://console.developers.google.com/apis/credentials. After you fix that - web browser page should open while running your code. You should authorize this application in browser (login to gmail account etc). After that it should work fine. – Evk Mar 20 '18 at 13:50
  • I have tried that just now, I have always got the "authorize" page, and that works, but when it returns to the console, it just displays the error `Error: Error:"invalid_client", Description:"Unauthorized", Uri:""` – Chad Mar 20 '18 at 13:56
  • To understand where the code was stopping I used `File.AppendAllText` and the last "log" entry is on the line before the `GoogleWebAuthorizationBroker.AuthorizeAsync()` nothing after that – Chad Mar 20 '18 at 13:58
  • @Chad and what is application type of your oauth2 client (web, android, ios, other)? I mean type you specify when creating oauth2 client in google dev console. – Evk Mar 20 '18 at 14:53
  • @Evk The key I am using in this "CLI project" is 'other' an 'installed' - I would use the 'web' type, but I can not get a consistent port number on the localhost url so I can not list it. I just swapped out the credentials for a service account one, and that makes the API call, but fails with "youtubeSignupRequiered"..... something is not working with the `GoogleWebAuthorizationBroker` – Chad Mar 20 '18 at 15:04
  • so, on a whim I created a new set of Oauth keys and the CLI app works... but I can't get the web version to work yet. – Chad Mar 20 '18 at 17:15
  • I am still looking for a working 'web' version. This code in a web environment does not work, it won't ask for the Authorization, and I don't know how to provide it the token – Chad Mar 20 '18 at 17:52

3 Answers3

6

All Credit to @iedoc for his code and answer here

A basic working Web project example

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>testing</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <p><asp:TextBox runat="server" ID="videoName" placeholder="Video Title" /></p>
        <p><asp:TextBox runat="server" ID="videoDesc" placeholder="Video Description" /></p>
        <p><asp:FileUpload ID="videoUpload" runat="server" /></p>
        <p><asp:Button id="saveDetails" Text="Update Chapel Group" runat="server" OnClick="saveDetails_click" /></p>
    </div>
    </form>
</body>
</html>

Default.aspx.cs

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Responses;

public partial class _Default : System.Web.UI.Page
{
    string vID = "none";
    public void Page_Load(object sender, EventArgs e)
    {

    }
    protected void saveDetails_click(object sender, EventArgs e)
    {
        if (Path.GetFileName(videoUpload.PostedFile.FileName) != "")
        {
            YouTubeUtilities ytU = new YouTubeUtilities("REFRESH", "SECRET", "CLIENT_ID"); // pass in your API codes here

            using (var fileStream = videoUpload.PostedFile.InputStream) // the selected post file
            {
                vID = ytU.UploadVideo(fileStream,videoName.Text,videoDesc.Text,"22",false);
            }
            Response.Write(vID);
        }

    }
}
public class YouTubeUtilities
{
    /*
     Instructions to get refresh token:
     * https://stackoverflow.com/questions/5850287/youtube-api-single-user-scenario-with-oauth-uploading-videos/8876027#8876027
     * 
     * When getting client_id and client_secret, use installed application, other (this will make the token a long term token)
     */
    private String CLIENT_ID { get; set; }
    private String CLIENT_SECRET { get; set; }
    private String REFRESH_TOKEN { get; set; }

    private String UploadedVideoId { get; set; }

    private YouTubeService youtube;

    public YouTubeUtilities(String refresh_token, String client_secret, String client_id)
    {
        CLIENT_ID = client_id;
        CLIENT_SECRET = client_secret;
        REFRESH_TOKEN = refresh_token;

        youtube = BuildService();
    }

    private YouTubeService BuildService()
    {
        ClientSecrets secrets = new ClientSecrets()
        {
            ClientId = CLIENT_ID,
            ClientSecret = CLIENT_SECRET
        };

        var token = new TokenResponse { RefreshToken = REFRESH_TOKEN };
        var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
            new GoogleAuthorizationCodeFlow.Initializer
            {
                ClientSecrets = secrets
            }),
            "user",
            token);

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

        //service.HttpClient.Timeout = TimeSpan.FromSeconds(360); // Choose a timeout to your liking
        return service;
    }

    public String UploadVideo(Stream stream, String title, String desc, String categoryId, Boolean isPublic)
    {
        var video = new Video();
        video.Snippet = new VideoSnippet();
        video.Snippet.Title = title;
        video.Snippet.Description = desc;
        video.Snippet.CategoryId = categoryId; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
        video.Status = new VideoStatus();
        video.Status.PrivacyStatus = isPublic ? "public" : "unlisted"; // "private" or "public" or unlisted

        //var videosInsertRequest = youtube.Videos.Insert(video, "snippet,status", stream, "video/*");
        var videosInsertRequest = youtube.Videos.Insert(video, "snippet,status", stream, "video/*");
        videosInsertRequest.ProgressChanged += insertRequest_ProgressChanged;
        videosInsertRequest.ResponseReceived += insertRequest_ResponseReceived;

        videosInsertRequest.Upload();

        return UploadedVideoId;
    }

    void insertRequest_ResponseReceived(Video video)
    {
        UploadedVideoId = video.Id;
        // video.ID gives you the ID of the Youtube video.
        // you can access the video from
        // http://www.youtube.com/watch?v={video.ID}
    }

    void insertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
    {
        // You can handle several status messages here.
        switch (progress.Status)
        {
            case UploadStatus.Failed:
                UploadedVideoId = "FAILED";
                break;
            case UploadStatus.Completed:
                break;
            default:
                break;
        }
    }
}

I hate .NET and I have been fighting with this for weeks. Some of the problems I encountered;

  1. a faulty pair of API Keys, they just didn't work. I guess it was a random bug.
  2. I also have a couple MP4's that I downloaded from Youtube that would not upload back, within creator studio they would say "Upload failed: Can't process file" I determined this by attempting to upload them in the youTube interface. (not through API)
  3. async vs. synchronous was causing me many issues I just don't understand.

I would like to improve this code to provide actual upload status/feedback but I think that would need to be done client side. I am not very good at C#/.NET

UPDATE here is my feedback code via server & client side - https://stackoverflow.com/a/49516167/3790921

Chad
  • 1,139
  • 17
  • 41
  • This doesn't really answer the question as its a web solution he states he is using a console application. Not to mention that this code for uploading a video not inserting a playlist dont you think you should address that? – Linda Lawton - DaImTo Mar 27 '18 at 09:26
  • he said "Any working example using C# (console/web) ?" - this is a working web example – Chad Mar 27 '18 at 12:35
  • I am curious then as to why you added a bounty then? Is it because your solution doesn't work? If so you should try mine it works for console applications. You know you cant be awarded a bounty on your own answer right? – Linda Lawton - DaImTo Mar 27 '18 at 12:36
  • I added the bounty because I was having the same problems... problems many people seem to be having, and there was no answers. The bounty was added after 2 weeks of me working at this, and almost a week before I posted this answer. I FINALLY am able to answer my own question, due to the other SO answer that Google/DuckDuckGo would not find as easily. In fact my latest revisions to this are even simpler. (everyone has a console app because that is what the example code Google provides is) – Chad Mar 27 '18 at 12:39
0

I have made some minor changes to your code, its best to get your initial example of Oauth2 working first. Its hard to tell why you are getting an access denied. Here is what i have changed.

  1. "user" i have changed to Environment.UserName this way your credentials name will be the name of the current logged in user.
  2. I have changed the location of your FileDataStore I am not all that sure that the code you were using was going to work. Mine will store the credentials in a new directory in the current working directory.

Info on the user parameter it is just used to create the credentials fine in the directory submitted to FileDataStore.

Google.Apis.Auth.OAuth2.Responses.TokenResponse-lilaw

My login user name is lilaw in this manner you can have credentials files for each user. Being that this is a console application that doesn't matter much.

What you should check if this does not work out of the box:

  1. When you created your client on Google developer console make sure it was type other. You cant use a console application with browser credentials and you cant use the YouTube API with a service account.
  2. Remember that the YouTube API channel based so when you login you pick a channel you will only have access to that channel.

Tip: If you want to log out the current user or force it to login again. Just change Environment.UserName to something else it will force it to login again

I have tested this code it works:

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace TestYoutube
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("YouTube Data API: Playlist Updates");
            Console.WriteLine("==================================");

            try
            {
                new Program().Run().Wait();
            }
            catch (AggregateException ex)
            {
                foreach (var e in ex.InnerExceptions)
                {
                    Console.WriteLine("Error: " + e.Message);
                }
            }

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        private async Task Run()
        {
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { YouTubeService.Scope.Youtube },
                    Environment.UserName,
                    CancellationToken.None,
                    new FileDataStore($"{Directory.GetCurrentDirectory()}/credentials")
                );
            }

            var youtubeService = new YouTubeService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = this.GetType().ToString()
            });

            // Create a new, private playlist in the authorized user's channel.
            var newPlaylist = new Playlist
            {
                Snippet = new PlaylistSnippet
                {
                    Title = "Test Playlist",
                    Description = "A playlist created with the YouTube API v3"
                },
                Status = new PlaylistStatus {PrivacyStatus = "public"}
            };
            newPlaylist = await youtubeService.Playlists.Insert(newPlaylist, "snippet,status").ExecuteAsync();

            // Add a video to the newly created playlist.
            var newPlaylistItem = new PlaylistItem
            {
                Snippet = new PlaylistItemSnippet
                {
                    PlaylistId = newPlaylist.Id,
                    ResourceId = new ResourceId
                    {
                        Kind = "youtube#video",
                        VideoId = "GNRMeaz6QRI"
                    }
                }
            };
            newPlaylistItem = await youtubeService.PlaylistItems.Insert(newPlaylistItem, "snippet").ExecuteAsync();

            Console.WriteLine("Playlist item id {0} was added to playlist id {1}.", newPlaylistItem.Id, newPlaylist.Id);
        }
    }
}

Once you have that working there are several examples here one is for upload video

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
-1

The following code sample calls the API's playlistItems.list method to retrieve a list of videos uploaded to the channel associated with the request. The code also calls the channels.list method with the mine parameter set to true to retrieve the playlist ID that identifies the channel's uploaded videos.

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;

namespace Google.Apis.YouTube.Samples
{
  /// <summary>
  /// YouTube Data API v3 sample: retrieve my uploads.
  /// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
  /// See https://developers.google.com/api-client-library/dotnet/get_started
  /// </summary>
  internal class MyUploads
  {
    [STAThread]
    static void Main(string[] args)
    {
      Console.WriteLine("YouTube Data API: My Uploads");
      Console.WriteLine("============================");

      try
      {
        new MyUploads().Run().Wait();
      }
      catch (AggregateException ex)
      {
        foreach (var e in ex.InnerExceptions)
        {
          Console.WriteLine("Error: " + e.Message);
        }
      }

      Console.WriteLine("Press any key to continue...");
      Console.ReadKey();
    }

    private async Task Run()
    {
      UserCredential credential;
      using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
      {
        credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
            GoogleClientSecrets.Load(stream).Secrets,
            // This OAuth 2.0 access scope allows for read-only access to the authenticated 
            // user's account, but not other types of account access.
            new[] { YouTubeService.Scope.YoutubeReadonly },
            "user",
            CancellationToken.None,
            new FileDataStore(this.GetType().ToString())
        );
      }

      var youtubeService = new YouTubeService(new BaseClientService.Initializer()
      {
        HttpClientInitializer = credential,
        ApplicationName = this.GetType().ToString()
      });

      var channelsListRequest = youtubeService.Channels.List("contentDetails");
      channelsListRequest.Mine = true;

      // Retrieve the contentDetails part of the channel resource for the authenticated user's channel.
      var channelsListResponse = await channelsListRequest.ExecuteAsync();

      foreach (var channel in channelsListResponse.Items)
      {
        // From the API response, extract the playlist ID that identifies the list
        // of videos uploaded to the authenticated user's channel.
        var uploadsListId = channel.ContentDetails.RelatedPlaylists.Uploads;

        Console.WriteLine("Videos in list {0}", uploadsListId);

        var nextPageToken = "";
        while (nextPageToken != null)
        {
          var playlistItemsListRequest = youtubeService.PlaylistItems.List("snippet");
          playlistItemsListRequest.PlaylistId = uploadsListId;
          playlistItemsListRequest.MaxResults = 50;
          playlistItemsListRequest.PageToken = nextPageToken;

          // Retrieve the list of videos uploaded to the authenticated user's channel.
          var playlistItemsListResponse = await playlistItemsListRequest.ExecuteAsync();

          foreach (var playlistItem in playlistItemsListResponse.Items)
          {
            // Print information about each video.
            Console.WriteLine("{0} ({1})", playlistItem.Snippet.Title, playlistItem.Snippet.ResourceId.VideoId);
          }

          nextPageToken = playlistItemsListResponse.NextPageToken;
        }
      }
    }
  }
}
D.P.
  • 101
  • 1
  • 4
  • 11