/// <summary> /// Convert subtitles to markdown /// </summary> /// <param name="outputDirectory">The directory where markdown files should be created.</param> /// <param name="azureStorageAccountKey"></param> /// <param name="youTubeClientId"></param> /// <param name="youTubeClientSecret"></param> /// <param name="console"></param> /// <returns></returns> static async Task Main( string outputDirectory = ".", string?azureStorageAccountKey = null, string?youTubeClientId = null, string?youTubeClientSecret = null, IConsole?console = null) { if (console is null) { throw new ArgumentNullException(nameof(console)); } var configBuilder = new ConfigurationBuilder(); configBuilder.AddEnvironmentVariables(); configBuilder.AddUserSecrets(typeof(Program).Assembly); var config = configBuilder.Build(); CloudStorageAccount storageAccount = StorageAccount.Get(azureStorageAccountKey, config); var tableClient = storageAccount.CreateCloudTableClient(); var streamVideoTables = tableClient.GetTableReference("streamvideos"); var youtubeSettingsTable = tableClient.GetTableReference("youtubesettings"); YouTubeService youTubeService = await YouTubeFactory.GetServiceAsync( new CloudTableDataStore(youtubeSettingsTable), config, youTubeClientId, youTubeClientSecret, YouTubeService.Scope.YoutubeForceSsl); foreach (VideoRow row in streamVideoTables.CreateQuery <VideoRow>() .Where(x => x.YouTubeVideoId != "" && x.YouTubeVideoId != "Unknown")) { if (!string.IsNullOrWhiteSpace(row.SubtitlesUrl)) { continue; } console.Out.WriteLine($"Converting markdown for YouTube video '{row.YouTubeVideoId}'"); if (await youTubeService.GetSubtitles(row.YouTubeVideoId !, CancellationToken.None) is { } subtitles) { if (!string.IsNullOrWhiteSpace(subtitles)) { console.Out.WriteLine(" Got subtitles"); string markdown = ConvertToMarkdown(row.YouTubeVideoId !, subtitles); string fileName = await WriteToFile(outputDirectory, markdown, row.YouTubeVideoId !, row.TwitchPublishedAt); console.Out.WriteLine($" Wrote markdown to '{fileName}'"); row.SubtitlesUrl = $"https://github.com/Keboo/YouTubeSubtitles/blob/master/Subtitles/{fileName}"; } else { console.Out.WriteLine($" YouTube video '{row.YouTubeVideoId}' not found"); row.SubtitlesUrl = "YouTube Video Removed"; } TableOperation insertOperation = TableOperation.Merge(row); TableResult _ = await streamVideoTables.ExecuteAsync(insertOperation); console.Out.WriteLine($" Updated table storage url with '{row.SubtitlesUrl}'"); } } }
public static async Task <int> Main( IConsole console, string?twitchUserId = null, string?twitchClientId = null, string?twitchClientSecret = null, string?azureStorageAccountKey = null, string?youTubeUsername = null, string?youTubePassword = null, string?youTubeRecoveryEmail = null) { var configBuilder = new ConfigurationBuilder(); configBuilder.AddEnvironmentVariables(); configBuilder.AddUserSecrets(typeof(Program).Assembly); var config = configBuilder.Build(); var section = config.GetSection(nameof(VideoConverter)); twitchUserId ??= section["TwitchUserId"] ?? throw new InvalidOperationException("No Twitch user id specified"); twitchClientId ??= section["TwitchClientId"] ?? throw new InvalidOperationException("No Twitch client id specified"); twitchClientSecret ??= section["TwitchClientSecret"] ?? throw new InvalidOperationException("No Twitch client secret specified"); var storageAccount = StorageAccount.Get(azureStorageAccountKey, config); var tableClient = storageAccount.CreateCloudTableClient(); var streamVideoTables = tableClient.GetTableReference("streamvideos"); var youtubeSettingsTable = tableClient.GetTableReference("youtubesettings"); TwitchAPI api = new(settings : new ApiSettings() { ClientId = twitchClientId, Secret = twitchClientSecret }); var httpClient = new HttpClient(); var twitchClinet = new Twitch(httpClient); console.Out.WriteLine("Retrieving videos from twitch"); var videoResponse = await api.Helix.Videos.GetVideoAsync(userId : twitchUserId); foreach (TwitchVideo video in videoResponse.Videos) { // Check if video exists in storage VideoRow?row = streamVideoTables.CreateQuery <VideoRow>() .Where(x => x.TwitchVideoId == video.Id) .FirstOrDefault(); if (!string.IsNullOrWhiteSpace(row?.YouTubeVideoId)) { console.Out.WriteLine($"Twitch video {video.Id} already has YouTube id '{row.YouTubeVideoId}'; skipping"); continue; } console.Out.WriteLine($"Downloading '{video.Title}' from {video.CreatedAt} - {video.Id} "); FileInfo?downloadedFilePath = await twitchClinet.DownloadVideoFileAsync(video.Id); if (downloadedFilePath is null) { console.Out.WriteLine($"Failed to download video file"); return(1); } console.Out.WriteLine($"Downloaded video to '{downloadedFilePath}'"); FileInfo?trimmedFilePath = await Ffmpeg.TrimLeadingSilence(downloadedFilePath); if (trimmedFilePath is null) { console.Error.WriteLine($"Failed to trim silence from '{downloadedFilePath}'"); return(1); } console.Out.WriteLine($"Trimmed silence '{trimmedFilePath}'"); var youtubeSection = config.GetSection("YouTube"); BrowserCredential creds = new( youTubeUsername ?? youtubeSection["Username"], youTubePassword ?? youtubeSection["Password"], youTubeRecoveryEmail ?? youtubeSection["RecoveryEmail"]); string youTubeId = await UploadVideoAsync(creds, trimmedFilePath, video); await DeleteFile(trimmedFilePath); if (string.IsNullOrWhiteSpace(youTubeId)) { console.Error.WriteLine($"Failed to upload '{trimmedFilePath}'"); return(1); } console.Out.WriteLine($"Uploaded to YouTube '{youTubeId}'"); DateTime recordingDate = video.GetRecordingDate() ?? DateTime.UtcNow.Date; if (row is null) { row = new VideoRow { PartitionKey = recordingDate.Year.ToString(), }; } row.TwitchVideoId = video.Id; row.TwitchPublishedAt = recordingDate; row.YouTubeVideoId = youTubeId; TableOperation insertOperation = TableOperation.InsertOrMerge(row); // Execute the operation. TableResult _ = await streamVideoTables.ExecuteAsync(insertOperation); break; } return(0); }