diff --git a/Tesses.YouTubeDownloader/DownloadLoop.cs b/Tesses.YouTubeDownloader/DownloadLoop.cs index 729b905..d18e442 100644 --- a/Tesses.YouTubeDownloader/DownloadLoop.cs +++ b/Tesses.YouTubeDownloader/DownloadLoop.cs @@ -22,6 +22,7 @@ namespace Tesses.YouTubeDownloader { while (!token.IsCancellationRequested) { + try{ bool hasAny; var (Video, Resolution) = Dequeue(out hasAny); if (hasAny) @@ -29,6 +30,10 @@ namespace Tesses.YouTubeDownloader await DownloadVideoAsync(Video, Resolution, token,new Progress(ReportProgress),true); } + }catch(Exception ex) + { + await GetLogger().WriteAsync(ex); + } } } @@ -48,6 +53,7 @@ namespace Tesses.YouTubeDownloader } private void ReportStartVideo(SavedVideo video, Resolution resolution, long length) { + GetLogger().WriteAsync(video).Wait(); Progress.Video = video; Progress.Progress = 0; Progress.ProgressRaw = 0; @@ -97,6 +103,7 @@ namespace Tesses.YouTubeDownloader private async Task DownloadVideoAsync(SavedVideo video, Resolution resolution, CancellationToken token=default(CancellationToken),IProgress progress=null,bool report=true) { + try{ switch (resolution) { case Resolution.Mux: @@ -112,6 +119,10 @@ namespace Tesses.YouTubeDownloader await DownloadVideoOnlyAsync(video,token,progress,report); break; } + }catch(Exception ex) + { + await GetLogger().WriteAsync(ex); + } } public async Task Continue(string path) diff --git a/Tesses.YouTubeDownloader/Logging.cs b/Tesses.YouTubeDownloader/Logging.cs new file mode 100644 index 0000000..6189a73 --- /dev/null +++ b/Tesses.YouTubeDownloader/Logging.cs @@ -0,0 +1,134 @@ + + + + +using System; +using YoutubeExplode; +using YoutubeExplode.Videos; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Net.Http; +using Newtonsoft.Json; +using System.IO; +using YoutubeExplode.Playlists; +using YoutubeExplode.Channels; +using System.Text; + +namespace Tesses.YouTubeDownloader +{ + public partial class TYTDStorage + { + private Mutex mtx0=new Mutex(); + private Logger _log=null; + public Logger GetLogger() + { + mtx0.WaitOne(); + if(_log == null) + { + _log = new Logger(this); + } + mtx0.ReleaseMutex(); + return _log; + } + } + public class LoggerProperties + { + public bool UseLogs {get;set;} + + public bool PrintVideoIds {get;set;} + + public bool PrintErrors {get;set;} + + public bool LogVideoIds {get;set;} + + + public bool AddDateInLog {get;set;} + } + public class Logger + { + Mutex mtx=new Mutex(); + private string _filename; + private TYTDStorage _storage; + + public LoggerProperties Properties {get;set;} + + private LoggerProperties GetProperties(TYTDStorage storage) + { + if(storage.FileExists("config/logger.json")) + { + string data=storage.ReadAllTextAsync("config/lggger.json").GetAwaiter().GetResult(); + return JsonConvert.DeserializeObject(data); + }else{ + LoggerProperties prop=new LoggerProperties(); + prop.AddDateInLog=true; + prop.LogVideoIds=true; + prop.PrintErrors =false; + prop.PrintVideoIds=true; + prop.UseLogs=true; + return prop; + } + } + internal Logger(TYTDStorage storage) + { + Properties = GetProperties(storage); + _storage=storage; + + string dateTime = DateTime.Now.ToString("yyyyMMdd_hhmmss"); + _filename = $"config/logs/{dateTime}.log"; + } + private void WriteStdErr(string message) + { + var col=Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine(message); + Console.ForegroundColor = col; + } + private void WriteStd(string message,bool error) + { + if(error) + WriteStdErr(message); + else + Console.WriteLine(message); + } + public async Task WriteAsync(string message,bool writeToConsole=false, bool isError=false,bool log=true) + { + + if(writeToConsole) WriteStd(message,isError); + if(!log || !Properties.UseLogs) return; + + // DateTime time = DateTime.Now; + var msg= new StringBuilder(); + if(Properties.AddDateInLog) + { + var dat=DateTime.Now; + msg.AppendLine($"{dat.ToShortDateString()} at {dat.ToShortTimeString()}:"); + } + msg.AppendLine(message); + mtx.WaitOne(); + using(var strm = await _storage.OpenOrCreateAsync(_filename)) + { + if(!strm.CanSeek) return; + + strm.Seek(0,SeekOrigin.End); + using(var sw = new StreamWriter(strm)) + { + await sw.WriteLineAsync(msg.ToString()); + } + } + mtx.ReleaseMutex(); + } + + public async Task WriteAsync(Exception ex) + { + await WriteAsync($"Exception Catched:\n{ex.ToString()}",Properties.PrintErrors,true); + } + public async Task WriteAsync(SavedVideo video) + { + await WriteAsync($"Download: {video.Title} with Id: {video.Id}",Properties.PrintVideoIds,false,Properties.LogVideoIds); + } + + } + +} \ No newline at end of file diff --git a/Tesses.YouTubeDownloader/TYTDBase.cs b/Tesses.YouTubeDownloader/TYTDBase.cs index f83d683..093d93d 100644 --- a/Tesses.YouTubeDownloader/TYTDBase.cs +++ b/Tesses.YouTubeDownloader/TYTDBase.cs @@ -371,11 +371,12 @@ namespace Tesses.YouTubeDownloader } } private static string[] resStr = new string[] {"Muxed","PreMuxed","AudioOnly","VideoOnly"}; - /// - /// Convert DirectoryName to Resolution - /// - /// - /// Video Resolution + + /// + /// Convert DirectoryName to Resolution + /// + /// + /// Video Resolution public static Resolution DirectoryToResolution(string folder) { int e= Array.IndexOf(resStr,folder); diff --git a/Tesses.YouTubeDownloader/TYTDFilesystem.cs b/Tesses.YouTubeDownloader/TYTDFilesystem.cs index b8f3eea..edb1c39 100644 --- a/Tesses.YouTubeDownloader/TYTDFilesystem.cs +++ b/Tesses.YouTubeDownloader/TYTDFilesystem.cs @@ -138,4 +138,136 @@ namespace Tesses.YouTubeDownloader Directory.Delete(dir,recursive); } } + /// + /// Abstracted directory from path + /// + public class TYTDPathDirectory : TYTDStorage + { + public TYTDPathDirectory(string path,HttpClient clt) : base(clt) + { + _path=path; + } + public TYTDPathDirectory(string path) : base() + { + _path=path; + } + string _path; + public string GetPath(string internalPath) + { + return Path.Combine(_path,internalPath.TrimStart('/')); + } + public override Task<(string Path, bool Delete)> GetRealUrlOrPathAsync(string path) + { + return Task.FromResult((GetPath(path),false)); + } + public override async Task MuxVideosAsync(SavedVideo video, string videoSrc, string audioSrc, string videoDest, IProgress progress = null, CancellationToken token = default) + { + if(string.IsNullOrWhiteSpace(FFmpeg)) + { + return false; + } + Func get_percent=(e)=>{ + if(string.IsNullOrWhiteSpace(e)) + return TimeSpan.Zero; + + int index=e.IndexOf("time="); + if(index < 0) + return TimeSpan.Zero; + + int spaceIndex=e.IndexOf(' ',index+5); + string j=e.Substring(index+5,spaceIndex-(index+5)); + + double val; + if(double.TryParse(j,out val)) + return TimeSpan.FromSeconds(val); + + return TimeSpan.ParseExact(j, "c", CultureInfo.InvariantCulture); + + //return TimeSpan.Zero; + }; + Directory.CreateDirectory(GetPath("TYTD_TEMP")); + bool ret; + + ret=await this.Convert(video,GetPath(videoSrc),GetPath(audioSrc),GetPath(videoDest),token,new Progress((e)=>{ + // time=\"\" + if(progress!=null) + { + var progr=get_percent(e).TotalSeconds / video.Duration.TotalSeconds; + progress.Report(progr / 4 + 0.50); + } + }),FFmpeg); + + Directory.Delete(GetPath("TYTD_TEMP"),true); + return ret; + } + public override async Task CreateAsync(string path) + { + return await Task.FromResult(File.Create(GetPath(path))); + } + + public override void CreateDirectory(string path) + { + Directory.CreateDirectory(GetPath(path)); + } + + public override async Task DirectoryExistsAsync(string path) + { + return await Task.FromResult(Directory.Exists(GetPath(path))); + } + + public override async IAsyncEnumerable EnumerateDirectoriesAsync(string path) + { + foreach(var dir in Directory.EnumerateDirectories(GetPath(path))) + { + yield return await Task.FromResult(Path.GetFileName(dir)); + } + } + + public override async IAsyncEnumerable EnumerateFilesAsync(string path) + { + foreach(var file in Directory.EnumerateFiles(GetPath(path))) + { + yield return await Task.FromResult(Path.GetFileName(file)); + } + } + + public override async Task FileExistsAsync(string path) + { + return await Task.FromResult(File.Exists(GetPath(path))); + } + + public override async Task OpenOrCreateAsync(string path) + { + if(File.Exists(path)) + { + return await Task.FromResult(File.OpenWrite(GetPath(path))); + } + return await CreateAsync(path); + } + + public override async Task OpenReadAsync(string path) + { + return await Task.FromResult(File.OpenRead(GetPath(path))); + } + + public override void RenameFile(string src, string dest) + { + File.Move(GetPath(src),GetPath(dest)); + } + + public override void MoveDirectory(string src, string dest) + { + Directory.Move(GetPath(src),GetPath(dest)); + } + + public override void DeleteFile(string file) + { + File.Delete(GetPath(file)); + } + + public override void DeleteDirectory(string dir, bool recursive = false) + { + Directory.Delete(GetPath(dir),recursive); + } + } } \ No newline at end of file diff --git a/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj b/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj index ca5a7c2..7f50fc7 100644 --- a/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj +++ b/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj @@ -7,9 +7,9 @@ Tesses.YouTubeDownloader Mike Nolan Tesses - 1.0.1.0 - 1.0.1.0 - 1.0.1.0 + 1.0.2.0 + 1.0.2.0 + 1.0.2.0 A YouTube Downloader LGPL-3.0-only true diff --git a/Tesses.YouTubeDownloader/VideoQueue.cs b/Tesses.YouTubeDownloader/VideoQueue.cs index 62fba6f..40634fd 100644 --- a/Tesses.YouTubeDownloader/VideoQueue.cs +++ b/Tesses.YouTubeDownloader/VideoQueue.cs @@ -36,8 +36,9 @@ namespace Tesses.YouTubeDownloader List Temporary =new List(); private async Task QueueLoop(CancellationToken token) { + while(!token.IsCancellationRequested) - { + { try{ IMediaContext context; lock(Temporary) { @@ -53,8 +54,14 @@ namespace Tesses.YouTubeDownloader if(context != null) { await context.FillQueue(this,QueueList); + } + } catch(Exception ex) + { + //did this so app can keep running + await GetLogger().WriteAsync(ex); } } + } } } \ No newline at end of file