From ea006dce85a46068fa429914b6b5338068ec8d39 Mon Sep 17 00:00:00 2001 From: Onur YILDIZ <41267854+onur-yildiz@users.noreply.github.com> Date: Sat, 30 Apr 2022 00:10:22 +0300 Subject: [PATCH] refactor: reorganize methods into classes for readability --- ConsoleRW/LoginPrompt.cs | 54 +++++++++++ ConsoleRW/StyledMessages.cs | 14 +++ FileSystem/FileSetup.cs | 17 ++++ Media/AudioCutter.cs | 17 ++++ Media/VideoDownloader.cs | 20 ++++ Models/Arguments/Options.cs | 7 +- Program.cs | 189 +----------------------------------- Puppeteer/Puppet.cs | 103 ++++++++++++++++++++ 8 files changed, 231 insertions(+), 190 deletions(-) create mode 100644 ConsoleRW/LoginPrompt.cs create mode 100644 ConsoleRW/StyledMessages.cs create mode 100644 FileSystem/FileSetup.cs create mode 100644 Media/AudioCutter.cs create mode 100644 Media/VideoDownloader.cs create mode 100644 Puppeteer/Puppet.cs diff --git a/ConsoleRW/LoginPrompt.cs b/ConsoleRW/LoginPrompt.cs new file mode 100644 index 0000000..4958511 --- /dev/null +++ b/ConsoleRW/LoginPrompt.cs @@ -0,0 +1,54 @@ +namespace Mipup.ConsoleRW +{ + internal class LoginPrompt + { + public static string[] GetCredentials() + { + string[] creds; + if (!File.Exists("./mipupc")) return PromptCredentials(); + creds = File.ReadAllLines("./mipupc").First().Split("||"); + if (creds.Length != 2) return PromptCredentials(); + return creds; + } + + static string[] PromptCredentials() + { + string? userId; + string? password = string.Empty; + do + { + Console.Clear(); + Console.Write("User ID: "); + userId = Console.ReadLine(); + + } while (String.IsNullOrEmpty(userId)); + + ConsoleKey key; + Console.Write("Password: "); + do + { + var keyInfo = Console.ReadKey(intercept: true); + key = keyInfo.Key; + + if (key == ConsoleKey.Backspace && password.Length > 0) + { + Console.Write("\b \b"); + password = password[0..^1]; + } + else if (!char.IsControl(keyInfo.KeyChar)) + { + Console.Write("*"); + password += keyInfo.KeyChar; + } + } while (key != ConsoleKey.Enter || password.Length == 0); + Console.Write("\nWould you like to save your credentials for the future? (YOUR CREDENTIALS WILL BE STORED AS PLAIN TEXT) [y/N]: "); + + if (new string[] { "y", "Y" }.Contains(Console.ReadLine())) + { + File.WriteAllText("./mipupc", $"{userId}||{password}"); + } + + return new string[] { userId, password }; + } + } +} diff --git a/ConsoleRW/StyledMessages.cs b/ConsoleRW/StyledMessages.cs new file mode 100644 index 0000000..795473b --- /dev/null +++ b/ConsoleRW/StyledMessages.cs @@ -0,0 +1,14 @@ +namespace Mipup.ConsoleRW +{ + internal class StyledMessages + { + public static void PrintAudioUrl(string url) + { + Console.Write("\nAudio URL: "); + var defaultConsoleColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine(url); + Console.ForegroundColor = defaultConsoleColor; + } + } +} diff --git a/FileSystem/FileSetup.cs b/FileSystem/FileSetup.cs new file mode 100644 index 0000000..f09aaf0 --- /dev/null +++ b/FileSystem/FileSetup.cs @@ -0,0 +1,17 @@ +using System.Diagnostics; + +namespace Mipup.FileSystem +{ + public class FileSetup + { + public static void CreateEmptyMediaFolder() + { + var resetMediaProcess = new Process(); + resetMediaProcess.StartInfo.FileName = "pwsh.exe"; + resetMediaProcess.StartInfo.Arguments = "/C if (Test-Path -Path './media') {rm -r -Force ./media}; mkdir media"; + resetMediaProcess.StartInfo.RedirectStandardOutput = true; + if (!resetMediaProcess.Start()) throw new Exception("Could not create media folder."); + resetMediaProcess.WaitForExit(); + } + } +} diff --git a/Media/AudioCutter.cs b/Media/AudioCutter.cs new file mode 100644 index 0000000..8ccc5c2 --- /dev/null +++ b/Media/AudioCutter.cs @@ -0,0 +1,17 @@ +using System.Diagnostics; + +namespace Mipup.Media +{ + internal class AudioCutter + { + public static void Crop(string filePath, string startDuration, int time) + { + var cropAudioProcess = new Process(); + cropAudioProcess.StartInfo.FileName = "./ffmpeg.exe"; + cropAudioProcess.StartInfo.Arguments = $"-i {filePath} -ss {startDuration} -t {time} -c:a mp3 ./media/output.mp3"; + cropAudioProcess.StartInfo.RedirectStandardOutput = true; + if (!cropAudioProcess.Start()) throw new Exception("Could not crop audio."); + cropAudioProcess.WaitForExit(); + } + } +} diff --git a/Media/VideoDownloader.cs b/Media/VideoDownloader.cs new file mode 100644 index 0000000..23cf686 --- /dev/null +++ b/Media/VideoDownloader.cs @@ -0,0 +1,20 @@ +using YoutubeExplode; +using YoutubeExplode.Videos.Streams; + +namespace Mipup.Media +{ + internal class VideoDownloader + { + public static async Task DownloadVideoAsync(string url) + { + var youtube = new YoutubeClient(); + var video = await youtube.Videos.GetAsync(url); + var streamManifest = await youtube.Videos.Streams.GetManifestAsync(video.Id); + var streamInfo = streamManifest.GetAudioStreams().GetWithLowestSize(); + + var fileNameWithExtension = $"video.{streamInfo.Container}"; + await youtube.Videos.Streams.DownloadAsync(streamInfo, $"./media/{fileNameWithExtension}"); + return fileNameWithExtension; + } + } +} diff --git a/Models/Arguments/Options.cs b/Models/Arguments/Options.cs index e5d983a..0708a48 100644 --- a/Models/Arguments/Options.cs +++ b/Models/Arguments/Options.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using CommandLine; namespace Mipup.Models.Arguments diff --git a/Program.cs b/Program.cs index 7516fba..ff709a4 100644 --- a/Program.cs +++ b/Program.cs @@ -1,202 +1,23 @@ -using System.Diagnostics; -using System.Security.Cryptography; -using YoutubeExplode; -using YoutubeExplode.Videos.Streams; -using PuppeteerSharp; -using PuppeteerSharp.Input; -using CommandLine; +using CommandLine; using Mipup.Models.Arguments; +using Mipup.ConsoleRW; namespace Mipup { - public class AudioCutter + public class Program { static async Task Main(string[] args) { try { var options = Parser.Default.ParseArguments(args).MapResult(o => o, (e) => { throw new Exception(e.First().ToString()); }); - await CutAndUploadAudio(options.Name, options.Url, options.StartDuration, options.Time); + var url = await Puppet.CutAndUploadAudio(options.Name, options.Url, options.StartDuration, options.Time); + StyledMessages.PrintAudioUrl(url); } catch (Exception ex) { Console.WriteLine(ex); } } - - public static async Task CutAndUploadAudio(string name, string url, string startDuration, int time) - { - CreateEmptyMediaFolder(); - var fileName = await DownloadVideoAsync(url); - CropAudio(fileName, startDuration, time); - - await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision); - var browser = await Puppeteer.LaunchAsync(new LaunchOptions - { - Headless = true - }); - - try - { - var page = await browser.NewPageAsync(); - var client = await page.Target.CreateCDPSessionAsync(); - var mouse = new Mouse(client, page.Keyboard); - - await Login(page); - await Upload(page, name); - var audioURL = await ExtractURL(page) ?? "Could not extract URL!."; - - Console.Write("\nAudio URL: "); - var defaultConsoleColor = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine(audioURL); - Console.ForegroundColor = defaultConsoleColor; - return audioURL; - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - return null; - } - finally - { - await browser.CloseAsync(); - } - } - - - static void CreateEmptyMediaFolder() - { - var resetMediaProcess = new Process(); - resetMediaProcess.StartInfo.FileName = "pwsh.exe"; - resetMediaProcess.StartInfo.Arguments = "/C if (Test-Path -Path './media') {rm -r -Force ./media}; mkdir media"; - resetMediaProcess.StartInfo.RedirectStandardOutput = true; - if (!resetMediaProcess.Start()) throw new Exception("Could not create media folder."); - resetMediaProcess.WaitForExit(); - } - - static async Task DownloadVideoAsync(string url) - { - var youtube = new YoutubeClient(); - var video = await youtube.Videos.GetAsync(url); - var streamManifest = await youtube.Videos.Streams.GetManifestAsync(video.Id); - var streamInfo = streamManifest.GetAudioStreams().GetWithLowestSize(); - - var videoName = $"video.{streamInfo.Container}"; - await youtube.Videos.Streams.DownloadAsync(streamInfo, $"./media/{videoName}"); - return videoName; - } - - static void CropAudio(string videoName, string startDuration, int time) - { - var cropAudioProcess = new Process(); - cropAudioProcess.StartInfo.FileName = "./ffmpeg.exe"; - cropAudioProcess.StartInfo.Arguments = $"-i ./media/{videoName} -ss {startDuration} -t {time} -c:a mp3 ./media/output.mp3"; - cropAudioProcess.StartInfo.RedirectStandardOutput = true; - if (!cropAudioProcess.Start()) throw new Exception("Could not crop audio."); - cropAudioProcess.WaitForExit(); - } - - static async Task Login(Page page) - { - Console.Clear(); - Console.WriteLine("Logging into myinstants..."); - var creds = GetCredentials(); - await page.GoToAsync("https://www.myinstants.com/accounts/login/?next=/new/"); - await page.TypeAsync("input[name=login]", creds[0]); - await page.TypeAsync("input[name=password]", creds[1]); - await page.ClickAsync("div.input-field>button[type=submit]"); - - //await page.WaitForNavigationAsync(); - await page.WaitForSelectorAsync("#id_name"); - Console.WriteLine("Logged in to myinstants."); - } - - static async Task Upload(Page page, string name) - { - Console.WriteLine("Uploading audio..."); - await page.TypeAsync("#id_name", name + Guid.NewGuid().ToString()); - var fileChooserDialogTask = page.WaitForFileChooserAsync(); - var termsCheckbox = await page.WaitForSelectorAsync("input[type=checkbox]"); - await Task.WhenAll(fileChooserDialogTask, page.ClickAsync("input[name=sound]")); - var fileChooser = await fileChooserDialogTask; - await fileChooser.AcceptAsync("./media/output.mp3"); - await page.EvaluateFunctionAsync("cb => cb.click()", termsCheckbox); - await page.ClickAsync("input[type=submit]"); - - //await page.WaitForNavigationAsync(); - await page.WaitForSelectorAsync("a.instant-link"); - Console.WriteLine("Uploaded Audio."); - } - - static async Task ExtractURL(Page page) - { - Console.WriteLine("Navigating to the audio page..."); - await page.EvaluateFunctionAsync(@" - () => { - const audioList = document.querySelectorAll('a.instant-link'); - audioList[audioList.length -1].click(); - } - "); - - //await page.WaitForNavigationAsync(); - await page.WaitForSelectorAsync("a[download]"); - Console.WriteLine("Navigated to audio page.\nExtracting URL..."); - - return await page.EvaluateFunctionAsync(@" - () => { - return document.querySelector('a[download]').href.toString(); - } - "); - } - - static string[] GetCredentials() - { - string[] creds; - if (!File.Exists("./mipupc")) return PromptCredentials(); - creds = File.ReadAllLines("./mipupc").First().Split("||"); - if (creds.Length != 2) return PromptCredentials(); - return creds; - } - - static string[] PromptCredentials() - { - string? userId; - string? password = string.Empty; - do - { - Console.Clear(); - Console.Write("User ID: "); - userId = Console.ReadLine(); - - } while (String.IsNullOrEmpty(userId)); - - ConsoleKey key; - Console.Write("Password: "); - do - { - var keyInfo = Console.ReadKey(intercept: true); - key = keyInfo.Key; - - if (key == ConsoleKey.Backspace && password.Length > 0) - { - Console.Write("\b \b"); - password = password[0..^1]; - } - else if (!char.IsControl(keyInfo.KeyChar)) - { - Console.Write("*"); - password += keyInfo.KeyChar; - } - } while (key != ConsoleKey.Enter || password.Length == 0); - Console.Write("\nWould you like to save your credentials for the future? (YOUR CREDENTIALS WILL BE STORED AS PLAIN TEXT) [y/N]: "); - - if (new string[] { "y", "Y" }.Contains(Console.ReadLine())) - { - File.WriteAllText("./mipupc", $"{userId}||{password}"); - } - - return new string[] {userId, password }; - } } } \ No newline at end of file diff --git a/Puppeteer/Puppet.cs b/Puppeteer/Puppet.cs new file mode 100644 index 0000000..cb0e809 --- /dev/null +++ b/Puppeteer/Puppet.cs @@ -0,0 +1,103 @@ +using PuppeteerSharp; +using PuppeteerSharp.Input; +using Mipup.FileSystem; +using Mipup.Media; +using Mipup.ConsoleRW; + +namespace Mipup +{ + internal class Puppet + { + public static async Task CutAndUploadAudio(string name, string url, string startDuration, int time) + { + Browser? browser = null; + try + { + FileSetup.CreateEmptyMediaFolder(); + var fileName = await VideoDownloader.DownloadVideoAsync(url); + var filePath = $"./media/{fileName}"; + AudioCutter.Crop(filePath, startDuration, time); + + Console.WriteLine("Downloading chromium..."); + await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision); + Console.Clear(); + browser = await Puppeteer.LaunchAsync(new LaunchOptions + { + Headless = true + }); + + var page = await browser.NewPageAsync(); + var client = await page.Target.CreateCDPSessionAsync(); + var mouse = new Mouse(client, page.Keyboard); + + Console.Clear(); + await Login(page); + await Upload(page, name); + return await ExtractURL(page); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + return "Could not extract URL!."; + } + finally + { + if (browser != null) await browser.CloseAsync(); + } + } + + static async Task Login(Page page) + { + Console.WriteLine("Logging into myinstants..."); + var creds = LoginPrompt.GetCredentials(); + await page.GoToAsync("https://www.myinstants.com/accounts/login/?next=/new/"); + await page.TypeAsync("input[name=login]", creds[0]); + await page.TypeAsync("input[name=password]", creds[1]); + await page.ClickAsync("div.input-field>button[type=submit]"); + + //await page.WaitForNavigationAsync(); + await page.WaitForSelectorAsync("#id_name"); + Console.WriteLine("Logged in to myinstants."); + } + + static async Task Upload(Page page, string name) + { + Console.WriteLine("Uploading audio..."); + await page.TypeAsync("#id_name", name + Guid.NewGuid().ToString()); + var fileChooserDialogTask = page.WaitForFileChooserAsync(); + var termsCheckbox = await page.WaitForSelectorAsync("input[type=checkbox]"); + await Task.WhenAll(fileChooserDialogTask, page.ClickAsync("input[name=sound]")); + var fileChooser = await fileChooserDialogTask; + await fileChooser.AcceptAsync("./media/output.mp3"); + await page.EvaluateFunctionAsync("cb => cb.click()", termsCheckbox); + await page.ClickAsync("input[type=submit]"); + + //await page.WaitForNavigationAsync(); + await page.WaitForSelectorAsync("a.instant-link"); + Console.WriteLine("Uploaded Audio."); + } + + static async Task ExtractURL(Page page) + { + Console.WriteLine("Navigating to the audio page..."); + await page.EvaluateFunctionAsync(@" + () => { + const audioList = document.querySelectorAll('a.instant-link'); + audioList[audioList.length -1].click(); + } + "); + + //await page.WaitForNavigationAsync(); + await page.WaitForSelectorAsync("a[download]"); + Console.WriteLine("Navigated to audio page.\nExtracting URL..."); + + var url = await page.EvaluateFunctionAsync(@" + () => { + return document.querySelector('a[download]').href.toString(); + } + "); + + return url ?? "Could not extract URL!."; + } + } +}