using PowerArgs; using Newtonsoft.Json.Linq; using System.Net; namespace SBT_Downloader { internal class Program { static void Main(string[] args) { Console.WriteLine("Welcome to the Sound Booth Theater Personal Archiving Tool!"); try { MyArgs parsed = Args.Parse(args); Console.WriteLine("Downloading book now..."); (List mp3Urls, List chapters) = ParseHar(parsed.Har); DownloadMp3s(mp3Urls, chapters, parsed); } catch (ArgException ex) { Console.WriteLine(ex.Message); Console.WriteLine(ArgUsage.GenerateUsageFromTemplate()); } } static (List mp3Urls, List chapters) ParseHar(string har) { try { string harContents = LoadHar(har); JObject harObject = JObject.Parse(harContents); List requestObjects = ParseRequests(harObject); List mp3Urls = ParseUrls(requestObjects); List chapters = ParseChapters(mp3Urls); return (mp3Urls, chapters); } catch (Exception ex) { Console.WriteLine(ex.Message); Environment.Exit(0); return (new List(), new List()); } } private static List ParseRequests(JObject harObject) { List requests = harObject["log"]["entries"] .Where(x => x.SelectToken("_resourceType").ToString() == "media") .Select(y => y.SelectToken("request")) .DistinctBy(z => z.SelectToken("url")).ToList(); return requests; } private static List ParseChapters(List mp3Urls) { List chapters = new(); foreach (string url in mp3Urls) { Uri uri = new(url); string fixedUri = uri.AbsoluteUri.Replace(uri.Query, string.Empty); List urlTokens = fixedUri.Split("/").ToList(); //string series = urlTokens[5]; //string title = urlTokens[6]; //string episode = urlTokens[7]; var chapterTokens = urlTokens.Last().Split("_").ToList(); chapterTokens.RemoveRange(0,5); string chapterLastToken = chapterTokens.Last().Split(".").First(); chapterTokens[^1] = chapterLastToken; chapters.Add(string.Join(" ", chapterTokens)); } return chapters; } static List ParseUrls(List requestList) { List urls = requestList.Select(x => x.SelectToken("url").ToString()).ToList(); Console.WriteLine(urls.Count + " total requests"); return urls; } private static void DownloadMp3s(List mp3Urls, List chapters, MyArgs arguments) { Parallel.ForEach( mp3Urls, new ParallelOptions { MaxDegreeOfParallelism = 10 }, (url, state, index) => { int chapterNumber = (int)index + 1; string chapterName = chapters[(int)index]; string fileName = $"{chapterNumber} {chapterName}.mp3"; using var client = new WebClient(); Console.WriteLine("Downloading file #" + index); client.DownloadFile(url, fileName); TagFile(chapterName, chapterNumber, chapters.Count, arguments); }); } private static void TagFile(string chapterName, int chapterNumber, int chapterCount, MyArgs arguments) { Console.WriteLine($"Tagging chapter {chapterNumber} ({chapterCount})"); var file = TagLib.File.Create($"{chapterNumber} {chapterName}.mp3"); file.Tag.Album = arguments.Title.IsNullOrEmpty() ? string.Empty : arguments.Title; List artistsList = new(); if (!arguments.Author.IsNullOrEmpty()) artistsList.Add(arguments.Author); if (!arguments.Narrator.IsNullOrEmpty()) artistsList.Add(arguments.Narrator); file.Tag.AlbumArtists = artistsList.ToArray(); file.Tag.Title = chapterName; file.Tag.Track = (uint)chapterNumber; file.Tag.TrackCount = (uint)chapterCount; file.Save(); } static string LoadHar(string harFileName) { string harContents; try { using StreamReader streamReader = new(harFileName); harContents = streamReader.ReadToEnd(); return harContents; } catch (Exception e) { Console.WriteLine("The file could not be read:"); Console.WriteLine(e.Message); Environment.Exit(0); return string.Empty; } } } }