Exportar arquivos para o YouTube em .NET C# com código aberto
Exportar vídeos para o YouTube de forma programática é essencial para automatizar o gerenciamento de conteúdo em vídeo. Este guia usa a biblioteca cliente oficial de código aberto do Google para .NET para fazer upload de vídeos pela YouTube Data API a partir de uma aplicação desktop ou de linha de comando, com autorização do usuário, relatórios de progresso e tratamento de erros.
Pré-requisitos
- .NET 6.0 ou posterior
- Projeto do Google Cloud com a YouTube Data API ativada
- Credenciais OAuth 2.0 do tipo Desktop app do Google Cloud Console (baixe seu arquivo
client_secrets.json), com uma tela de consentimento OAuth e um usuário de teste autorizado, se aplicável - Pacote NuGet Google.Apis.YouTube.v3
Configurar o cliente da API do YouTube
Primeiro, instale o pacote NuGet necessário:
dotnet add package Google.Apis.YouTube.v3 --version 1.76.0.4262
Use o método de fábrica a seguir para criar uma instância do serviço do YouTube. A primeira
autorização abre um navegador para que o proprietário do canal possa conceder acesso. Uploads
para o YouTube exigem credenciais OAuth de usuário; contas de serviço não podem substituir esse
fluxo. O diretório de tokens armazena tokens de atualização e deve ser privado para o usuário da
aplicação. Os dois blocos da classe partial abaixo pertencem ao mesmo projeto.
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.YouTube.v3;
using Google.Apis.Util.Store;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
public partial class YouTubeUploader : IDisposable
{
private readonly YouTubeService _youtubeService;
private YouTubeUploader(YouTubeService youtubeService)
{
_youtubeService = youtubeService;
}
public static async Task<YouTubeUploader> CreateAsync(string credentialsPath, string tokenDirectory)
{
using var stream = File.OpenRead(credentialsPath);
var clientSecrets = GoogleClientSecrets.FromStream(stream).Secrets;
var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
new[] { YouTubeService.Scope.YoutubeUpload },
"channel-owner",
CancellationToken.None,
new FileDataStore(tokenDirectory, true));
var youtubeService = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "YOUR_APP_NAME"
});
return new YouTubeUploader(youtubeService);
}
public void Dispose() => _youtubeService.Dispose();
}
Fazer upload de vídeos
O método abaixo gerencia uploads de vídeos com acompanhamento do progresso e tratamento abrangente de erros:
using System;
using System.IO;
using System.Threading.Tasks;
using Google.Apis.Upload;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
public partial class YouTubeUploader
{
public async Task<string> UploadVideoAsync(
string filePath,
string title,
string description,
string[] tags,
IProgress<IUploadProgress>? progress = null)
{
var video = new Video
{
Snippet = new VideoSnippet
{
Title = title,
Description = description,
Tags = tags,
CategoryId = "22" // People & Blogs category
},
Status = new VideoStatus
{
PrivacyStatus = "private" // or "public", "unlisted"
}
};
using var fileStream = File.OpenRead(filePath);
var videosInsertRequest = _youtubeService.Videos.Insert(
video,
"snippet,status",
fileStream,
"video/*");
videosInsertRequest.ChunkSize = ResumableUpload.MinimumChunkSize;
if (progress != null)
{
videosInsertRequest.ProgressChanged += progress.Report;
}
try
{
var uploadResponse = await videosInsertRequest.UploadAsync();
uploadResponse.ThrowOnFailure();
if (uploadResponse.Status != UploadStatus.Completed ||
string.IsNullOrEmpty(videosInsertRequest.ResponseBody?.Id))
{
throw new InvalidOperationException("Video upload did not complete.");
}
return videosInsertRequest.ResponseBody.Id;
}
catch (Google.GoogleApiException ex) when (ex.Error != null &&
(ex.Error.Code == 403 || ex.Error.Code == 429 || ex.Error.Code == 503))
{
throw new InvalidOperationException("YouTube rejected the upload. Check channel access, project quota, and service availability.", ex);
}
finally
{
if (progress != null) videosInsertRequest.ProgressChanged -= progress.Report;
}
}
}
Gerenciar cotas e limites de requisições
Confira os limites do seu projeto e a documentação atual de cotas do YouTube
antes de definir um orçamento local. Os uploads têm seu próprio grupo de cotas, e as cotas diárias
são redefinidas à meia-noite no horário do Pacífico. O mecanismo de proteção a seguir limita a
concorrência e reserva um orçamento configurável antes de cada operação, incluindo tentativas
malsucedidas. Compartilhe uma instância por grupo dentro de um processo.
Esse mecanismo opera em memória, portanto reinicializações, novas tentativas do SDK e outras
instâncias da aplicação ainda exigem monitoramento centralizado de cotas. Passe o custo atual da
operação como quotaCost.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class QuotaManager : IDisposable
{
private readonly SemaphoreSlim _uploadSemaphore;
private readonly Dictionary<DateTime, int> _quotaUsage;
private readonly int _dailyLimit;
private readonly TimeZoneInfo _quotaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles");
public QuotaManager(int dailyLimit, int maxConcurrentUploads = 3)
{
if (dailyLimit <= 0 || maxConcurrentUploads <= 0) throw new ArgumentOutOfRangeException();
_dailyLimit = dailyLimit;
_uploadSemaphore = new SemaphoreSlim(maxConcurrentUploads);
_quotaUsage = new Dictionary<DateTime, int>();
}
public async Task<T> ExecuteWithQuotaAsync<T>(
Func<Task<T>> operation,
int quotaCost)
{
if (quotaCost <= 0 || quotaCost > _dailyLimit) throw new ArgumentOutOfRangeException(nameof(quotaCost));
await _uploadSemaphore.WaitAsync();
try
{
lock (_quotaUsage)
{
var today = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, _quotaTimeZone).Date;
if (!_quotaUsage.ContainsKey(today))
{
_quotaUsage.Clear();
_quotaUsage[today] = 0;
}
if (quotaCost > _dailyLimit - _quotaUsage[today])
{
throw new InvalidOperationException("Daily upload budget would be exceeded");
}
_quotaUsage[today] += quotaCost;
}
return await operation();
}
finally
{
_uploadSemaphore.Release();
}
}
public void Dispose() => _uploadSemaphore.Dispose();
}
Boas práticas para uso em produção
- Implemente uma lógica de novas tentativas com espera exponencial para lidar com erros transitórios, como problemas de rede ou limites temporários de requisições.
- Armazene suas credenciais com segurança para proteger seus segredos OAuth 2.0.
- Monitore o progresso dos uploads para fornecer feedback e solucionar problemas em tempo real.
- Garanta um tratamento robusto de erros para lidar com cotas excedidas, falhas de autenticação e restrições de tamanho de arquivo ou de rede.
Implementar a lógica de novas tentativas
O SDK gerencia novas tentativas de uploads retomáveis. Use a função auxiliar abaixo apenas para
operações que possam ser repetidas com segurança, como requisições de leitura. Repetir uma operação
videos.insert inteira pode criar vídeos duplicados se o primeiro upload tiver sido
concluído, mas sua resposta tiver sido perdida.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Google;
public class RetryExamples
{
public async Task<T> RetryWithExponentialBackoff<T>(
Func<Task<T>> operation,
int maxAttempts = 3)
{
if (maxAttempts <= 0) throw new ArgumentOutOfRangeException(nameof(maxAttempts));
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
try
{
return await operation();
}
catch (Exception ex) when (IsTransientException(ex))
{
if (attempt == maxAttempts) throw;
var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt));
await Task.Delay(delay);
}
}
throw new Exception("Retry attempts exhausted");
}
private bool IsTransientException(Exception ex)
{
return ex is HttpRequestException ||
(ex is GoogleApiException gex && (gex.Error?.Code == 429 || gex.Error?.Code == 503));
}
}
Armazenar credenciais com segurança
Configure YOUTUBE_CREDENTIALS_PATH no ambiente do seu processo para apontar para o arquivo JSON
baixado do cliente desktop. Mantenha esse arquivo e o diretório de tokens fora do controle de
versão, restrinja o acesso ao sistema de arquivos e use um repositório gerenciado de segredos em
implantações hospedadas. Esta função auxiliar apenas localiza o arquivo; ela não criptografa as
credenciais. Aplicações web precisam do fluxo OAuth do Google no lado do servidor, em vez do fluxo
via navegador para desktop mostrado aqui.
using System;
public class SecureCredentialManager
{
public string GetCredentialsPath()
{
var path = Environment.GetEnvironmentVariable("YOUTUBE_CREDENTIALS_PATH");
if (string.IsNullOrEmpty(path))
{
throw new InvalidOperationException("YOUTUBE_CREDENTIALS_PATH is not configured");
}
return path;
}
}
Monitorar o progresso dos uploads
using System;
using Google.Apis.Upload;
public class UploadProgressHandler : IProgress<IUploadProgress>
{
public void Report(IUploadProgress progress)
{
var status = progress.Status switch
{
UploadStatus.Uploading => $"Uploading: {progress.BytesSent} bytes sent",
UploadStatus.Failed => "Upload failed",
UploadStatus.Completed => "Upload completed",
_ => $"Status: {progress.Status}"
};
Console.WriteLine(status);
}
}
Conclusão
Exportar arquivos para o YouTube em .NET C# exige práticas atualizadas de autenticação, gerenciamento cuidadoso de cotas e tratamento robusto de erros. Ao integrar a biblioteca cliente das APIs do Google com carregamento assíncrono de credenciais e seguir boas práticas para implantações em produção, você pode criar uma solução resiliente de upload de vídeos. Para simplificar a exportação de arquivos e o processamento de vídeos em escala, considere usar o serviço de exportação de arquivos da Transloadit.
