Dateiexport zu YouTube in .NET C# mit Open-Source-Bibliotheken
Videos programmatisch zu YouTube zu exportieren, ist für die Automatisierung der Verwaltung von Videoinhalten unerlässlich. Diese Anleitung nutzt die offizielle Open-Source-Client-Bibliothek von Google für .NET, um Videos aus einer Desktop- oder Kommandozeilenanwendung in die YouTube Data API hochzuladen, inklusive Nutzerautorisierung, Fortschrittsanzeige und Fehlerbehandlung.
Voraussetzungen
- .NET 6.0 oder neuer
- Google Cloud Project mit aktivierter YouTube Data API
- OAuth-2.0-Zugangsdaten vom Typ Desktop app aus der Google Cloud Console (laden Sie die Datei
client_secrets.jsonherunter), mit einem OAuth-Zustimmungsbildschirm und einem autorisierten Testnutzer, sofern zutreffend - NuGet-Paket Google.Apis.YouTube.v3
Den YouTube-API-Client einrichten
Installieren Sie zunächst das erforderliche NuGet-Paket:
dotnet add package Google.Apis.YouTube.v3 --version 1.76.0.4262
Verwenden Sie die folgende Factory-Methode, um eine YouTube-Service-Instanz zu erstellen. Bei der
ersten Autorisierung öffnet sich ein Browser, damit der Kanalinhaber den Zugriff gewähren kann. Für
YouTube-Uploads sind OAuth-Zugangsdaten von Nutzern erforderlich; Dienstkonten können diesen Ablauf
nicht ersetzen. Das Token-Verzeichnis speichert Refresh-Tokens und muss für den Anwendungsnutzer
privat sein. Die beiden partial-Klassenblöcke weiter unten gehören zum selben Projekt.
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();
}
Videos hochladen
Die folgende Methode wickelt Video-Uploads mit Fortschrittsverfolgung und umfassender Fehlerbehandlung ab:
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;
}
}
}
Kontingente und Ratenbegrenzungen handhaben
Prüfen Sie die Limits Ihres Projekts und die aktuelle YouTube-Kontingentdokumentation,
bevor Sie ein lokales Budget festlegen. Uploads haben ein eigenes Kontingent-Bucket, und die
Tageskontingente werden um Mitternacht Pacific Time zurückgesetzt. Der folgende Schutzmechanismus
begrenzt die Nebenläufigkeit und reserviert vor jedem Vorgang ein konfigurierbares Budget, auch bei
fehlgeschlagenen Versuchen. Verwenden Sie innerhalb eines Prozesses eine gemeinsame Instanz pro
Bucket. Es handelt sich um einen In-Memory-Schutz, daher erfordern Neustarts, SDK-Retries und
weitere Anwendungsinstanzen weiterhin eine zentrale Kontingentüberwachung. Übergeben Sie die
aktuellen Kosten des Vorgangs als 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();
}
Bewährte Verfahren für den Produktiveinsatz
- Implementieren Sie eine Retry-Logik mit exponentiellem Backoff, um vorübergehende Fehler wie Netzwerkprobleme oder temporäre Ratenbegrenzungen abzufangen.
- Speichern Sie Ihre Zugangsdaten sicher, um Ihre OAuth-2.0-Secrets zu schützen.
- Überwachen Sie den Upload-Fortschritt, um in Echtzeit Rückmeldung zu geben und Probleme zu beheben.
- Sorgen Sie für eine robuste Fehlerbehandlung, die überschrittene Kontingente, fehlgeschlagene Authentifizierung sowie Beschränkungen durch Dateigröße oder Netzwerk abdeckt.
Retry-Logik implementieren
Das SDK übernimmt die Retries für fortsetzbare Uploads. Verwenden Sie den folgenden Helper nur für
Vorgänge, die sich gefahrlos wiederholen lassen, etwa Leseanfragen. Wenn Sie einen kompletten
Vorgang videos.insert erneut ausführen, können doppelte Videos entstehen, falls der erste Upload
erfolgreich war, seine Antwort aber verloren ging.
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));
}
}
Zugangsdaten sicher speichern
Konfigurieren Sie YOUTUBE_CREDENTIALS_PATH in Ihrer Prozessumgebung so, dass der Wert auf die heruntergeladene
JSON-Datei des Desktop-Clients verweist. Halten Sie diese Datei und das Token-Verzeichnis aus der
Versionsverwaltung heraus, beschränken Sie den Dateisystemzugriff und nutzen Sie für gehostete
Deployments einen verwalteten Secret Store. Dieser Helper ermittelt lediglich den Speicherort der
Datei; er verschlüsselt keine Zugangsdaten. Webanwendungen benötigen den serverseitigen OAuth-Flow
von Google anstelle des hier gezeigten Desktop-Browser-Flows.
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;
}
}
Upload-Fortschritt überwachen
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);
}
}
Fazit
Der Export von Dateien zu YouTube in .NET C# erfordert aktuelle Authentifizierungsverfahren, ein sorgfältiges Kontingentmanagement und eine robuste Fehlerbehandlung. Wenn Sie die Client-Bibliothek der Google APIs mit asynchronem Laden der Zugangsdaten kombinieren und bewährte Verfahren für den Produktiveinsatz befolgen, können Sie eine widerstandsfähige Lösung für Video-Uploads aufbauen. Für einen schlankeren Ansatz bei Dateiexporten und Videoverarbeitung im großen Maßstab sollten Sie Transloadits Dateiexport-Service in Betracht ziehen.
