Как загрузить видео на YouTube с помощью Google.Apis.YouTube.v3 и C#?

Я создал приложение console, используя C#. Что будет upload Video с локального диска на youtube. Я создал новое приложение в Google API, используя эту ссылку. Я также установил все необходимые packages, используя nuget. Когда я запускаю свое приложение, я получаю сообщение об ошибке «Отказано в доступе». Я не могу найти проблему.

Я получаю сообщение об ошибке в методе Task Run().

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

Нужно ли передавать параметру пользователя имя пользователя gmail?

Любой рабочий пример с использованием С# (консоль/веб)?

Помощь приветствуется.


person SHEKHAR SHETE    schedule 16.06.2017    source источник
comment
@ Чад, так в чем проблема? Где фейлы? Тот же доступ запрещен?   -  person Evk    schedule 20.03.2018
comment
Я получаю Inner Exception 1: TokenResponseException: Error:"invalid_client", Description:"Unauthorized", Uri:"" как ошибку, см. мой пост здесь stackoverflow.com/questions/49364545/ Я вижу один и тот же пример кода и вопросы снова и снова без ответов. Интересно, работает ли вообще библиотека .NET? Я занимаюсь этим уже больше недели.   -  person Chad    schedule 20.03.2018
comment
@Chad Я только что попробовал, и у меня отлично работает (не все, только часть авторизации). Поэтому я и спрашиваю, где именно у вас ошибка. На какой линии? Авторизация или позже?   -  person Evk    schedule 20.03.2018
comment
в Task Run() мой код выполняется в строке/методе credential = await GoogleWebAuthorizationBroker.AuthorizeAsync()   -  person Chad    schedule 20.03.2018
comment
@Chad, ваша ошибка предполагает, что у вас есть проблемы с идентификатором клиента\секретом. Попробуйте инициализировать их напрямую: AuthorizeAsync(new ClientSecrets {ClientId = "your id", ClientSecret = "secret"}...). Используйте учетные данные OAuth2.0 с этой страницы: console.developers.google.com/apis/credentials. После того, как вы это исправите, страница веб-браузера должна открываться во время выполнения вашего кода. Вы должны авторизовать это приложение в браузере (войти в учетную запись gmail и т. д.). После этого он должен работать нормально.   -  person Evk    schedule 20.03.2018
comment
Я попробовал это только что, у меня всегда есть страница авторизации, и это работает, но когда он возвращается в консоль, он просто отображает ошибку Error: Error:"invalid_client", Description:"Unauthorized", Uri:""   -  person Chad    schedule 20.03.2018
comment
Чтобы понять, где останавливался код, я использовал File.AppendAllText, а последняя запись в журнале находится в строке перед GoogleWebAuthorizationBroker.AuthorizeAsync(), после чего ничего не было.   -  person Chad    schedule 20.03.2018
comment
@Chad, а какой тип приложения у вашего клиента oauth2 (веб, android, ios, другое)? Я имею в виду тип, который вы указываете при создании клиента oauth2 в консоли разработчика Google.   -  person Evk    schedule 20.03.2018
comment
@Evk Ключ, который я использую в этом проекте CLI, - это «другой» и «установленный» - я бы использовал тип «веб», но я не могу получить согласованный номер порта в URL-адресе локального хоста, поэтому я не могу перечислить его. Я только что поменял учетные данные на учетную запись службы, и это вызывает вызов API, но не работает с youtubeSignupRequiered..... что-то не работает с GoogleWebAuthorizationBroker   -  person Chad    schedule 20.03.2018
comment
Итак, по прихоти я создал новый набор ключей Oauth, и приложение CLI работает... но я пока не могу заставить работать веб-версию.   -  person Chad    schedule 20.03.2018
comment
Я все еще ищу рабочую «веб-версию». Этот код в веб-среде не работает, он не будет запрашивать авторизацию, и я не знаю, как предоставить ему токен.   -  person Chad    schedule 20.03.2018


Ответы (3)


Все заслуги @iedoc за его код и ответ здесь

Базовый рабочий пример веб-проекта

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

Я ненавижу .NET и уже несколько недель борюсь с этим. Некоторые из проблем, с которыми я столкнулся;

  1. неисправная пара ключей API, они просто не работали. Я предполагаю, что это был случайный баг.
  2. У меня также есть пара MP4, которые я скачал с Youtube, которые не загружались обратно, в студии создателей они говорили: «Ошибка загрузки: не удается обработать файл». Я определил это, попытавшись загрузить их в интерфейсе YouTube. (не через API)
  3. асинхронный и синхронный вызывал у меня много проблем, которых я просто не понимаю.

Я хотел бы улучшить этот код, чтобы предоставить фактический статус загрузки/обратную связь, но я думаю, что это нужно сделать на стороне клиента. Я не очень хорошо разбираюсь в C#/.NET

ОБНОВЛЕНИЕ вот мой код обратной связи через сервер и клиент - https://stackoverflow.com/a/49516167/3790921

person Chad    schedule 22.03.2018
comment
На самом деле это не отвечает на вопрос, поскольку это веб-решение, которое он заявляет, что использует консольное приложение. Не говоря уже о том, что этот код для загрузки видео без вставки плейлиста, вы не думаете, что вам следует решить эту проблему? - person DaImTo; 27.03.2018
comment
он сказал Любой рабочий пример с использованием С# (консоль/веб)? - это рабочий веб-пример - person Chad; 27.03.2018
comment
Тогда мне любопытно, почему вы тогда добавили награду? Это потому, что ваше решение не работает? Если это так, вы должны попробовать мой, он работает для консольных приложений. Вы знаете, что не можете получить награду за свой собственный ответ, верно? - person DaImTo; 27.03.2018
comment
Я добавил вознаграждение, потому что у меня были те же проблемы... проблемы, похоже, возникают у многих людей, и не было ответов. Награда была добавлена ​​после 2 недель моей работы над этим и почти за неделю до того, как я опубликовал этот ответ. Я НАКОНЕЦ-ТО могу ответить на свой вопрос из-за другого ответа SO, который Google / DuckDuckGo не нашел бы так легко. На самом деле, мои последние версии еще проще. (у всех есть консольное приложение, потому что это пример кода, который предоставляет Google) - person Chad; 27.03.2018

Я внес некоторые незначительные изменения в ваш код, лучше сначала получить ваш первоначальный пример Oauth2. Трудно сказать, почему вы получаете отказ в доступе. Вот что я изменил.

  1. «user» я изменил на Environment.UserName, таким образом, ваше имя учетных данных будет именем текущего вошедшего в систему пользователя.
  2. Я изменил местоположение вашего FileDataStore. Я не совсем уверен, что код, который вы использовали, сработает. Мой будет хранить учетные данные в новом каталоге в текущем рабочем каталоге.

Информация о пользовательском параметре используется только для создания учетных данных в каталоге, отправленном в FileDataStore.

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

Мое имя пользователя для входа в систему — lilaw, поэтому вы можете иметь файлы учетных данных для каждого пользователя. То, что это консольное приложение, не имеет большого значения.

Что вы должны проверить, если это не работает из коробки:

  1. Когда вы создали свой клиент в консоли разработчика Google, убедитесь, что он относится к другому типу. Вы не можете использовать консольное приложение с учетными данными браузера и не можете использовать API YouTube с служебной учетной записью.
  2. Помните, что канал API YouTube основан на том, что когда вы входите в систему, вы выбираете канал, у вас будет доступ только к этому каналу.

Совет: Если вы хотите выйти из текущего пользователя или заставить его снова войти в систему. Просто измените Environment.UserName на что-то другое, это заставит его снова войти в систему.

Я проверил этот код, он работает:

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

После того, как вы заработаете, есть несколько примеров, один из которых для загрузки видео

person DaImTo    schedule 27.03.2018

В следующем примере кода вызывается метод API playlistItems.list для получения списка видео, загруженных на канал, связанный с запросом. Код также вызывает метод channels.list с параметром mine, для которого задано значение true, чтобы получить идентификатор плейлиста, который идентифицирует видео, загруженные на канал.

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;
        }
      }
    }
  }
}
person D.P.    schedule 25.03.2018
comment
Вопрос о том, чтобы вставить список воспроизведения, не выбирая его, это не отвечает на вопрос. - person DaImTo; 27.03.2018