From cb3e6c6d1a5b11b41f55b365215f36143a4385d3 Mon Sep 17 00:00:00 2001 From: Michael Nolan Date: Sat, 9 Apr 2022 19:19:07 -0500 Subject: [PATCH] Version 1.0.1 --- Tesses.YouTubeDownloader.YTGET/Program.cs | 109 +++++++++ Tesses.YouTubeDownloader.YTGET/ProgressBar.cs | 90 ++++++++ .../Tesses.YouTubeDownloader.YTGET.csproj | 14 ++ Tesses.YouTubeDownloader/TYTDClient.cs | 213 ++++++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 Tesses.YouTubeDownloader.YTGET/Program.cs create mode 100644 Tesses.YouTubeDownloader.YTGET/ProgressBar.cs create mode 100644 Tesses.YouTubeDownloader.YTGET/Tesses.YouTubeDownloader.YTGET.csproj create mode 100644 Tesses.YouTubeDownloader/TYTDClient.cs diff --git a/Tesses.YouTubeDownloader.YTGET/Program.cs b/Tesses.YouTubeDownloader.YTGET/Program.cs new file mode 100644 index 0000000..5504a05 --- /dev/null +++ b/Tesses.YouTubeDownloader.YTGET/Program.cs @@ -0,0 +1,109 @@ +using Newtonsoft.Json; +using Tesses.YouTubeDownloader; +using YoutubeExplode.Videos; + +namespace Tesses.YouTubeDownloader.YTGET +{ + public class Program + { + public static HttpClient Client; + public static async Task Main(string[] args) + { + Client=new HttpClient(); + var vars=Environment.GetEnvironmentVariables(); + if(vars.Contains("TYTD_DIR")) + { + Environment.CurrentDirectory=vars["TYTD_DIR"].ToString(); + } + Resolution resolution=Resolution.PreMuxed; + if(args.Length ==0) + { + while(true) + { + Console.Write("> "); + string res=Console.ReadLine(); + if(!string.IsNullOrWhiteSpace(res)) + { + if(res.Equals("exit")) + { + return; + }else if(res.Equals("clear")){ + Console.Clear(); + }else if(res.Equals("save")) { + Console.Write("Type Video Id or Url: "); + string idOrUrl=Console.ReadLine(); + Console.Write("Type destination directory: "); + string dest=Console.ReadLine(); + + } else if(res.Equals("help")) + { + Console.WriteLine("exit: Exit the program"); + Console.WriteLine("clear: clear screen"); + Console.WriteLine("resmux: set the Video Resolution to Mux"); + Console.WriteLine("respremux: set the Video Resolution to PreMuxed"); + Console.WriteLine("resaudio: set the Video Resolution to AudioOnly"); + Console.WriteLine("resvidonly: set the Video Resolution to VideoOnly"); + Console.WriteLine("Url or Id: Video Id or Url"); + Console.WriteLine(); + } + else if(res.Equals("resmux")) + { + Console.WriteLine("Video Resolution has been set to Mux"); + resolution=Resolution.Mux; + }else if(res.Equals("respremux")) + { + Console.WriteLine("Video Resolution has been set to PreMuxed"); + resolution = Resolution.PreMuxed; + }else if(res.Equals("resaudio")) + { + Console.WriteLine("Video Resolution has been set to AudioOnly"); + resolution = Resolution.AudioOnly; + }else if(res.Equals("resvidonly")) + { + Console.WriteLine("Video Resolution has been set to VideoOnly"); + resolution = Resolution.VideoOnly; + }else{ + await Download(res); + } + + + } + } + } + foreach(var arg in args) + { + await Download(arg); + } + } + public static async Task Download(string url,Resolution resolution=Resolution.PreMuxed,CancellationToken token=default(CancellationToken)) + { + //download video + + TYTDCurrentDirectory currentDirectory=new TYTDCurrentDirectory(); + currentDirectory.CreateDirectories(); + VideoId? id=VideoId.TryParse(url); + if(id.HasValue) + { + var savedVideo= await currentDirectory.GetSavedVideoAsync(id.Value); + if(savedVideo !=null) + { + Console.Write($"{savedVideo.Title}: "); + + //bool first=true; + using(ProgressBar bar=new ProgressBar()) + { + bar.Report(0.0); + await currentDirectory.DownloadNoQueue(savedVideo,resolution,token,new Progress((p)=>{ + bar.Report(p); + })); + bar.Report(0.99); + bar.Report(1.0); + + } + Console.WriteLine(); + + } + } + } + } +} \ No newline at end of file diff --git a/Tesses.YouTubeDownloader.YTGET/ProgressBar.cs b/Tesses.YouTubeDownloader.YTGET/ProgressBar.cs new file mode 100644 index 0000000..cb3bc09 --- /dev/null +++ b/Tesses.YouTubeDownloader.YTGET/ProgressBar.cs @@ -0,0 +1,90 @@ +using System; +using System.Text; +using System.Threading; + +/// +/// An ASCII progress bar +/// +public class ProgressBar : IDisposable, IProgress { + private const int blockCount = 10; + private readonly TimeSpan animationInterval = TimeSpan.FromSeconds(1.0 / 8); + private const string animation = @"|/-\"; + + private readonly Timer timer; + + private double currentProgress = 0; + private string currentText = string.Empty; + private bool disposed = false; + private int animationIndex = 0; + + public ProgressBar() { + timer = new Timer(TimerHandler); + + // A progress bar is only for temporary display in a console window. + // If the console output is redirected to a file, draw nothing. + // Otherwise, we'll end up with a lot of garbage in the target file. + if (!Console.IsOutputRedirected) { + ResetTimer(); + } + } + + public void Report(double value) { + // Make sure value is in [0..1] range + value = Math.Max(0, Math.Min(1, value)); + Interlocked.Exchange(ref currentProgress, value); + } + + private void TimerHandler(object state) { + lock (timer) { + if (disposed) return; + + int progressBlockCount = (int) (currentProgress * blockCount); + int percent = (int) (currentProgress * 100); + string text = string.Format("[{0}{1}] {2,3}% {3}", + new string('#', progressBlockCount), new string('-', blockCount - progressBlockCount), + percent, + animation[animationIndex++ % animation.Length]); + UpdateText(text); + + ResetTimer(); + } + } + + private void UpdateText(string text) { + // Get length of common portion + int commonPrefixLength = 0; + int commonLength = Math.Min(currentText.Length, text.Length); + while (commonPrefixLength < commonLength && text[commonPrefixLength] == currentText[commonPrefixLength]) { + commonPrefixLength++; + } + + // Backtrack to the first differing character + StringBuilder outputBuilder = new StringBuilder(); + outputBuilder.Append('\b', currentText.Length - commonPrefixLength); + + // Output new suffix + outputBuilder.Append(text.Substring(commonPrefixLength)); + + // If the new text is shorter than the old one: delete overlapping characters + int overlapCount = currentText.Length - text.Length; + if (overlapCount > 0) { + outputBuilder.Append(' ', overlapCount); + outputBuilder.Append('\b', overlapCount); + } + + Console.Write(outputBuilder); + currentText = text; + } + + private void ResetTimer() { + timer.Change(animationInterval, TimeSpan.FromMilliseconds(-1)); + } + + public void Dispose() { + lock (timer) { + disposed = true; + UpdateText(string.Empty); + } + } + +} \ No newline at end of file diff --git a/Tesses.YouTubeDownloader.YTGET/Tesses.YouTubeDownloader.YTGET.csproj b/Tesses.YouTubeDownloader.YTGET/Tesses.YouTubeDownloader.YTGET.csproj new file mode 100644 index 0000000..3415ca0 --- /dev/null +++ b/Tesses.YouTubeDownloader.YTGET/Tesses.YouTubeDownloader.YTGET.csproj @@ -0,0 +1,14 @@ + + + + + + + + Exe + net6.0 + enable + enable + + + diff --git a/Tesses.YouTubeDownloader/TYTDClient.cs b/Tesses.YouTubeDownloader/TYTDClient.cs new file mode 100644 index 0000000..d2cbdd0 --- /dev/null +++ b/Tesses.YouTubeDownloader/TYTDClient.cs @@ -0,0 +1,213 @@ +using Newtonsoft.Json; +using YoutubeExplode.Videos.Streams; +using System.Linq; +using System; +using System.Threading.Tasks; +using YoutubeExplode.Videos; +using System.Threading; +using YoutubeExplode.Exceptions; +using System.Collections.Generic; +using System.IO; +using YoutubeExplode.Channels; +using YoutubeExplode.Playlists; +using System.Net.Http; + +namespace Tesses.YouTubeDownloader +{ + public class TYTDClient : TYTDBase,IDownloader + { + public TYTDClient(string url) + { + client=new HttpClient(); + client.BaseAddress=new Uri(url); + } + + public TYTDClient(HttpClient clt,string url) + { + client = clt; + client.BaseAddress=new Uri(url); + } + public TYTDClient(HttpClient clt, Uri uri) + { + client=clt; + client.BaseAddress=uri; + } + public TYTDClient(Uri uri) + { + client=new HttpClient(); + client.BaseAddress=uri; + } + HttpClient client; + public async Task AddChannelAsync(ChannelId id, Resolution resolution = Resolution.PreMuxed) + { + try{ + await client.GetAsync($"/api/v2/AddChannel?v={id.Value}&res={resolution.ToString()}"); + }catch(Exception ex) + { + _=ex; + } + } + + public async Task AddPlaylistAsync(PlaylistId id, Resolution resolution = Resolution.PreMuxed) + { + try{ + await client.GetAsync($"/api/v2/AddPlaylist?v={id.Value}&res={resolution.ToString()}"); + }catch(Exception ex) + { + _=ex; + } + } + + public async Task AddUserAsync(UserName userName, Resolution resolution = Resolution.PreMuxed) + { + try{ + await client.GetAsync($"/api/v2/AddUser?v={userName.Value}&res={resolution.ToString()}"); + }catch(Exception ex) + { + _=ex; + } + } + + public async Task AddVideoAsync(VideoId id, Resolution resolution = Resolution.PreMuxed) + { + try{ + await client.GetAsync($"/api/v2/AddVideo?v={id.Value}&res={resolution.ToString()}"); + }catch(Exception ex) + { + _=ex; + } + } + + public override async Task DirectoryExistsAsync(string path) + { + try{ + string v=await client.GetStringAsync($"/api/Storage/DirectoryExists/{path}"); + return v=="true"; + }catch(Exception ex) + { + _=ex; + } + return false; + } + + public async override IAsyncEnumerable EnumerateDirectoriesAsync(string path) + { + List items=null; + try{ + string v=await client.GetStringAsync($"/api/Storage/GetDirectory/{path}"); + items=JsonConvert.DeserializeObject>(v); + }catch(Exception ex) + { + _=ex; + } + + if(items==null) + { + yield break; + }else{ + foreach(var item in items) + { + yield return await Task.FromResult(item); + } + } + } + + public async override IAsyncEnumerable EnumerateFilesAsync(string path) + { + List items=null; + try{ + string v=await client.GetStringAsync($"/api/Storage/GetFiles/{path}"); + items=JsonConvert.DeserializeObject>(v); + }catch(Exception ex) + { + _=ex; + } + + if(items==null) + { + yield break; + }else{ + foreach(var item in items) + { + yield return await Task.FromResult(item); + } + } + } + + public async override Task FileExistsAsync(string path) + { + try{ + string v=await client.GetStringAsync($"/api/Storage/FileExists/{path}"); + return v=="true"; + }catch(Exception ex) + { + _=ex; + } + return false; + } + private async Task> GetQueueListAsync() + { + + try{ + string v=await client.GetStringAsync("/api/v2/QueueList"); + return JsonConvert.DeserializeObject>(v); + }catch(Exception ex) + { + _=ex; + } + + return new List<(SavedVideo video,Resolution resolution)>(); + } + private async Task GetProgressAsync() + { + + try{ + string v=await client.GetStringAsync("/api/v2/Progress"); + return JsonConvert.DeserializeObject(v); + }catch(Exception ex) + { + _=ex; + } + + return null; + } + public SavedVideoProgress GetProgress() + { + return GetProgressAsync().GetAwaiter().GetResult(); + } + + public IReadOnlyList<(SavedVideo Video, Resolution Resolution)> GetQueueList() + { + return GetQueueListAsync().GetAwaiter().GetResult(); + } + + public override async Task GetLengthAsync(string path) + { + try{ + var item=await client.GetAsync($"/api/Storage/File/{path}"); + return item.Content.Headers.ContentLength.GetValueOrDefault(); + }catch(Exception ex) + { + _=ex; + } + + return 0; + } + + public async override Task OpenReadAsync(string path) + { + + try{ + Stream v=await client.GetStreamAsync($"/api/Storage/File/{path}"); + return v; + }catch(Exception ex) + { + _=ex; + } + + return Stream.Null; + } + + + } +} \ No newline at end of file