예제 #1
0
        /// <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}'");
                }
            }
        }
예제 #2
0
        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);
        }