diff --git a/.gitignore b/.gitignore
index da4ad96..c54a85f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,4 +143,6 @@ Tesses.YouTubeDownloader.Net6/config/
Tesses.YouTubeDownloader.Net6/Playlist/
Tesses.YouTubeDownloader.Net6/Channel/
Tesses.YouTubeDownloader.Net6/Subscriptions/
+Tesses.YouTubeDownloader.Net6/Download/
+Tesses.YouTubeDownloader.Net6/FileInfo/
push
diff --git a/Tesses.YouTubeDownloader.ExtensionLoader/Tesses.YouTubeDownloader.ExtensionLoader.csproj b/Tesses.YouTubeDownloader.ExtensionLoader/Tesses.YouTubeDownloader.ExtensionLoader.csproj
index 5babc7d..2040300 100644
--- a/Tesses.YouTubeDownloader.ExtensionLoader/Tesses.YouTubeDownloader.ExtensionLoader.csproj
+++ b/Tesses.YouTubeDownloader.ExtensionLoader/Tesses.YouTubeDownloader.ExtensionLoader.csproj
@@ -11,9 +11,9 @@
Tesses.YouTubeDownloader.ExtensionLoader
Mike Nolan
Tesses
- 1.1.1
- 1.1.1
- 1.1.1
+ 1.1.2
+ 1.1.2
+ 1.1.2
Load Extensions into TYTD (Not Tested)
LGPL-3.0-only
true
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Config.cs b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Config.cs
new file mode 100644
index 0000000..8f2ad8c
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Config.cs
@@ -0,0 +1,24 @@
+using Newtonsoft.Json;
+
+public class TYTDConfiguration
+{
+ public TYTDConfiguration()
+ {
+ Url = "http://127.0.0.1:3252/";
+ LocalFiles=Environment.CurrentDirectory;
+ }
+ public string Url {get;set;}
+
+ public string LocalFiles {get;set;}
+
+ public static TYTDConfiguration Load()
+ {
+ if(!File.Exists("proxy.json")) return new TYTDConfiguration();
+ var res= JsonConvert.DeserializeObject(File.ReadAllText("proxy.json"));
+ if(res != null)
+ {
+ return res;
+ }
+ return new TYTDConfiguration();
+ }
+}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Program.cs b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Program.cs
new file mode 100644
index 0000000..5eee806
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Program.cs
@@ -0,0 +1,23 @@
+using Tesses.YouTubeDownloader;
+using Tesses.YouTubeDownloader.Server;
+using Tesses.WebServer;
+using Newtonsoft.Json;
+
+var config=TYTDConfiguration.Load();
+Environment.CurrentDirectory=config.LocalFiles;
+var c=new HttpClient();
+TYTDCurrentDirectory currentDirectory=new TYTDCurrentDirectory(c);
+TYTDClient client=new TYTDClient(c,config.Url);
+
+TYTDDownloaderStorageProxy proxy=new TYTDDownloaderStorageProxy();
+proxy.Storage = currentDirectory;
+proxy.Downloader=client;
+
+TYTDServer server=new TYTDServer(proxy);
+server.RootServer.Server=new StaticServer("WebSite");
+currentDirectory.CanDownload=false;
+HttpServerListener listener=new HttpServerListener(new System.Net.IPEndPoint(System.Net.IPAddress.Any,3252),server.InnerServer);
+currentDirectory.StartLoop();
+TYTDStorage.FFmpeg ="/usr/bin/ffmpeg";
+Console.WriteLine("Almost Ready to Listen");
+await listener.ListenAsync(CancellationToken.None);
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Tesses.YouTubeDownloader.ServerProxy.csproj b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Tesses.YouTubeDownloader.ServerProxy.csproj
new file mode 100644
index 0000000..12432f2
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/Tesses.YouTubeDownloader.ServerProxy.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/proxy.json b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/proxy.json
new file mode 100644
index 0000000..8b424a3
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.ServerProxy/proxy.json
@@ -0,0 +1,4 @@
+{
+ "Url": "http://10.137.42.142:3252/",
+ "LocalFiles": "/media/mike/PhotoDrive/wii-vids/working"
+}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/StringUtils.cs b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/StringUtils.cs
new file mode 100644
index 0000000..3dbf34e
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/StringUtils.cs
@@ -0,0 +1,18 @@
+using System.Text;
+
+namespace Tesses.YouTubeDownloader.Tools.Common
+{
+ public static class StringUtils
+ {
+ public static string GetSafeFileName(this string filename)
+ {
+ StringBuilder b=new StringBuilder(filename);
+ foreach(var badChr in "\\\"\'/?*<>|:")
+ {
+ b.Replace(badChr.ToString(),"");
+ }
+ if(b.Length == 0) return "file";
+ return b.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/SymlinkGenerator.cs b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/SymlinkGenerator.cs
new file mode 100644
index 0000000..208dfdd
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/SymlinkGenerator.cs
@@ -0,0 +1,96 @@
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System;
+namespace Tesses.YouTubeDownloader.Tools.Common
+{
+ public static class SymlinkGenerator
+ {
+ [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
+ static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
+ private static void createHardLink(string destPath,string srcPath)
+ {if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ CreateHardLink(destPath,srcPath,IntPtr.Zero);
+ }else{
+ using(var p=new Process())
+ {
+
+ p.StartInfo.FileName = "ln";
+ p.StartInfo.ArgumentList.Add(srcPath);
+ p.StartInfo.ArgumentList.Add(destPath);
+
+ if(p.Start()){ p.WaitForExit();}
+ }
+ }
+ }
+ public static async Task GenerateHardLinks(TYTDStorage storage,string dest="GoodFileNames",Resolution res=Resolution.PreMuxed,bool verbose=false)
+ {
+ Directory.CreateDirectory(dest);
+ await foreach(var item in storage.GetVideosAsync())
+ {
+ if(await item.VideoExistsAsync(storage,res))
+ {
+ var (path,delete)= await storage.GetRealUrlOrPathAsync(await BestStreams.GetPathResolution(storage,item,res));
+ string? ext=Path.GetExtension(path);
+
+ string defaultExt = res == Resolution.Mux ? ".mkv" : ".mp4";
+ if(string.IsNullOrWhiteSpace(ext))
+ {
+ ext=defaultExt;
+ }
+ string destPathMkv=Path.Combine(dest,$"{item.Title.GetSafeFileName()}-{item.Id}{defaultExt}");
+ string destPath=Path.Combine(dest,$"{item.Title.GetSafeFileName()}-{item.Id}{ext}");
+
+ if(File.Exists(destPathMkv) && destPathMkv != destPath)
+ {
+ File.Delete(destPathMkv);
+ createHardLink(destPath,path);
+ if(verbose)
+ Console.WriteLine($"Changed: {item.Title} {defaultExt} -> {ext}");
+ }
+ if(!File.Exists(destPath))
+ {
+ createHardLink(destPath,path);
+ if(verbose)
+ Console.WriteLine(item.Title);
+ }
+ }
+ }
+
+ }
+ public static async Task GenerateSymlinks(TYTDBase storage,string dest="GoodFileNames",Resolution res=Resolution.PreMuxed,bool verbose=false)
+ {
+ Directory.CreateDirectory(dest);
+ await foreach(var item in storage.GetVideosAsync())
+ {
+ if(await item.VideoExistsAsync(storage,res))
+ {
+ var (path,delete)= await storage.GetRealUrlOrPathAsync(await BestStreams.GetPathResolution(storage,item,res));
+ string? ext=Path.GetExtension(path);
+
+ string defaultExt = res == Resolution.Mux ? ".mkv" : ".mp4";
+ if(string.IsNullOrWhiteSpace(ext))
+ {
+ ext=defaultExt;
+ }
+ string destPathMkv=Path.Combine(dest,$"{item.Title.GetSafeFileName()}-{item.Id}{defaultExt}");
+ string destPath=Path.Combine(dest,$"{item.Title.GetSafeFileName()}-{item.Id}{ext}");
+
+ if(File.Exists(destPathMkv) && destPathMkv != destPath)
+ {
+ File.Delete(destPathMkv);
+ File.CreateSymbolicLink(destPath,path);
+ if(verbose)
+ Console.WriteLine($"Changed: {item.Title} {defaultExt} -> {ext}");
+ }
+ if(!File.Exists(destPath))
+ {
+ File.CreateSymbolicLink(destPath,path);
+ if(verbose)
+ Console.WriteLine(item.Title);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/TYTDContext.cs b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/TYTDContext.cs
new file mode 100644
index 0000000..3eaa6a9
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/TYTDContext.cs
@@ -0,0 +1,34 @@
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System;
+using Tesses.YouTubeDownloader.SFTP;
+namespace Tesses.YouTubeDownloader.Tools.Common
+{
+ public static class TYTDOpener
+ {
+ public static TYTDBase? GetTYTDBase(string p)
+ {
+ Uri? uri;
+
+ if(Uri.TryCreate(p,UriKind.Absolute,out uri))
+ {
+ if(uri.IsFile)
+ {
+ return new TYTDPathDirectory(uri.LocalPath);
+ }
+ if(uri.Scheme == "sftp")
+ {
+ return new SSHFS(uri);
+ }
+ if(uri.Scheme == "http" || uri.Scheme == "https")
+ {
+ return new TYTDClient(uri);
+ }
+ }else{
+ if(!string.IsNullOrWhiteSpace(p)) return new TYTDPathDirectory(p);
+ }
+ return null;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/Tesses.YouTubeDownloader.Tools.Common.csproj b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/Tesses.YouTubeDownloader.Tools.Common.csproj
new file mode 100644
index 0000000..681133a
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.Common/Tesses.YouTubeDownloader.Tools.Common.csproj
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.LinkCreator/Program.cs b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.LinkCreator/Program.cs
new file mode 100644
index 0000000..8be3e2d
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.LinkCreator/Program.cs
@@ -0,0 +1,83 @@
+using Tesses.YouTubeDownloader.Tools.Common;
+using Tesses.YouTubeDownloader;
+using System;
+using System.IO;
+
+Resolution res=Resolution.PreMuxed;
+
+bool verbose=false;
+bool isSymlink = false;
+List _args=new List();
+foreach(var arg in args)
+{
+ bool any=false;
+ if(arg == "-h" || arg == "--help")
+ {
+ _args.Clear();
+ break;
+ }
+ if( (arg.Length >= 2 && arg[1] != '-' && arg[0] == '-' && arg.Contains("s") )|| arg == "--symbolic")
+ {
+ any=true;
+ isSymlink=true;
+ }
+ if((arg.Length >= 2 && arg[1] != '-' && arg[0] == '-' && arg.Contains("m") ) || arg == "--mux" )
+ {
+ any=true;
+ res = Resolution.Mux;
+ }
+ if((arg.Length >= 2 && arg[1] != '-' && arg[0] == '-' && arg.Contains("a") ) || arg == "--audio-only")
+ {
+ any=true;
+ res = Resolution.AudioOnly;
+
+ }
+ if ((arg.Length >= 2 && arg[1] != '-' && arg[0] == '-' && arg.Contains("V") ) || arg=="--video-only")
+ {
+ any=true;
+ res = Resolution.VideoOnly;
+
+ }
+ if ((arg.Length >= 2 && arg[1] != '-' && arg[0] == '-' && arg.Contains("v") ) || arg=="--verbose")
+ {
+ any=true;
+ verbose=true;
+
+ }
+ if(!any)
+ _args.Add(arg);
+
+}
+string[] argv = _args.ToArray();
+if(argv.Length < 2)
+{
+ string app = Path.GetFileNameWithoutExtension(Environment.GetCommandLineArgs()[0]);
+
+ Console.WriteLine($"usage: {app} [-smaVv] []");
+ Console.WriteLine();
+ Console.WriteLine("Options:");
+ Console.WriteLine(" -s, --symbolic make symbolic links instead of hard links");
+ Console.WriteLine(" -m, --mux set resolution to Mux");
+ Console.WriteLine(" -a, --audio-only set resolution to AudioOnly");
+ Console.WriteLine(" -V, --video-only set resolution to VideoOnly");
+ Console.WriteLine(" -h, --help show this help");
+ Console.WriteLine(" -v, --verbose print video names");
+ Console.WriteLine();
+ Console.WriteLine("Positional Arguments:");
+ Console.WriteLine(" Working the folder containing the Info Directory for TYTD. (required)");
+ Console.WriteLine(" Destination the folder to create links within. (required)");
+
+
+}else{
+
+
+Environment.CurrentDirectory=argv[0];
+TYTDCurrentDirectory currentDirectory=new TYTDCurrentDirectory();
+currentDirectory.CanDownload=false;
+
+if(isSymlink){
+await SymlinkGenerator.GenerateSymlinks(currentDirectory,argv[1],res,verbose);
+}else{
+await SymlinkGenerator.GenerateHardLinks(currentDirectory,argv[1],res,verbose);
+}
+}
diff --git a/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.LinkCreator/Tesses.YouTubeDownloader.Tools.LinkCreator.csproj b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.LinkCreator/Tesses.YouTubeDownloader.Tools.LinkCreator.csproj
new file mode 100644
index 0000000..a3577b1
--- /dev/null
+++ b/Tesses.YouTubeDownloader.Extras/Tesses.YouTubeDownloader.Tools/Tesses.YouTubeDownloader.Tools.LinkCreator/Tesses.YouTubeDownloader.Tools.LinkCreator.csproj
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/Tesses.YouTubeDownloader.Server/Class1.cs b/Tesses.YouTubeDownloader.Server/Class1.cs
index 11a2c68..adbf4f5 100644
--- a/Tesses.YouTubeDownloader.Server/Class1.cs
+++ b/Tesses.YouTubeDownloader.Server/Class1.cs
@@ -25,6 +25,79 @@ namespace Tesses.YouTubeDownloader.Server
public override async Task GetAsync(ServerContext ctx)
{
string path=ctx.UrlAndQuery;
+
+ if(path.StartsWith("/AddPlaylistRes/"))
+ { string id_res=path.Substring(16);
+ string[] id_res_split = id_res.Split(new char[] {'/'},2,StringSplitOptions.RemoveEmptyEntries);
+ if(id_res_split.Length ==2)
+ {
+ int num;
+ if(int.TryParse(id_res_split[0],out num))
+ {
+ if(num < 0) num=1;
+ if(num > 3) num=1;
+
+ await downloader1.AddPlaylistAsync(id_res_split[1],(Resolution)num);
+ }
+ }
+ }
+ if(path.StartsWith("/AddChannelRes/"))
+ { string id_res=path.Substring(15);
+ string[] id_res_split = id_res.Split(new char[] {'/'},2,StringSplitOptions.RemoveEmptyEntries);
+ if(id_res_split.Length ==2)
+ {
+ int num;
+ if(int.TryParse(id_res_split[0],out num))
+ {
+ if(num < 0) num=1;
+ if(num > 3) num=1;
+
+ await downloader1.AddChannelAsync(id_res_split[1],(Resolution)num);
+ }
+ }
+ }
+
+ if(path.StartsWith("/AddChannel/"))
+ {
+ await downloader1.AddChannelAsync(path.Substring(12),Resolution.PreMuxed);
+
+ }
+ if(path.StartsWith("/AddPlaylist/"))
+ {
+ await downloader1.AddPlaylistAsync(path.Substring(13),Resolution.PreMuxed);
+
+ }
+ if(path.StartsWith("/AddVideoRes/"))
+ { string id_res=path.Substring(13);
+ string[] id_res_split = id_res.Split(new char[] {'/'},2,StringSplitOptions.RemoveEmptyEntries);
+ if(id_res_split.Length ==2)
+ {
+ int num;
+ if(int.TryParse(id_res_split[0],out num))
+ {
+ if(num < 0) num=1;
+ if(num > 3) num=1;
+
+ await downloader1.AddVideoAsync(id_res_split[1],(Resolution)num);
+ }
+ }
+ }
+
+ if(path.StartsWith("/AddVideo/"))
+ {
+ //string id_res=path.Substring(12);
+ //string[] id_res_split = id_res.Split(new char[] {'/'},2,StringSplitOptions.RemoveEmptyEntries);
+ //if(id_res_split.Length ==2)
+ //{
+
+ await downloader1.AddVideoAsync(path.Substring(10),Resolution.PreMuxed);
+
+ // }
+ // await ctx.SendTextAsync(
+ // $"You Will Be Redirected in 5 Sec
\n"
+ //);
+ await ctx.SendRedirectAsync("/");
+ }
if(path.StartsWith("/AddItemRes/"))
{
string id_res=path.Substring(12);
@@ -43,7 +116,7 @@ namespace Tesses.YouTubeDownloader.Server
// await ctx.SendTextAsync(
// $"You Will Be Redirected in 5 Sec
\n"
//);
- await ctx.SendRedirectAsync("/");
+
}
if(path.StartsWith("/AddItem/"))
{
@@ -58,8 +131,59 @@ namespace Tesses.YouTubeDownloader.Server
// await ctx.SendTextAsync(
// $"You Will Be Redirected in 5 Sec
\n"
//);
- await ctx.SendRedirectAsync("/");
+
}
+ if(path.StartsWith("/AddUserRes/"))
+ {
+ string id_res=path.Substring(12);
+ string[] id_res_split = id_res.Split(new char[] {'/'},2,StringSplitOptions.RemoveEmptyEntries);
+ if(id_res_split.Length ==2)
+ {
+ int num;
+ if(int.TryParse(id_res_split[0],out num))
+ {
+ if(num < 0) num=1;
+ if(num > 3) num=1;
+
+ await downloader1.AddUserAsync(id_res_split[1],(Resolution)num);
+ }
+ }
+ // await ctx.SendTextAsync(
+ // $"You Will Be Redirected in 5 Sec
\n"
+ //);
+
+ }
+ if(path.StartsWith("/AddUser/"))
+ {
+ //string id_res=path.Substring(12);
+ //string[] id_res_split = id_res.Split(new char[] {'/'},2,StringSplitOptions.RemoveEmptyEntries);
+ //if(id_res_split.Length ==2)
+ //{
+
+ await downloader1.AddUserAsync(path.Substring(9),Resolution.PreMuxed);
+
+ // }
+ // await ctx.SendTextAsync(
+ // $"You Will Be Redirected in 5 Sec
\n"
+ //);
+
+ }
+ if(path.StartsWith("/AddFile/"))
+ {
+ //string id_res=path.Substring(12);
+ //string[] id_res_split = id_res.Split(new char[] {'/'},2,StringSplitOptions.RemoveEmptyEntries);
+ //if(id_res_split.Length ==2)
+ //{
+
+ await downloader1.AddFileAsync(path.Substring(9));
+
+ // }
+ // await ctx.SendTextAsync(
+ // $"You Will Be Redirected in 5 Sec
\n"
+ //);
+
+ }
+ await ctx.SendRedirectAsync("/");
}
}
internal class ApiStorage : Tesses.WebServer.Server
@@ -332,6 +456,7 @@ namespace Tesses.YouTubeDownloader.Server
AddBoth("/AddUser",AddUser);
AddBoth("/AddPlaylist",AddPlaylist);
AddBoth("/AddVideo",AddVideo);
+ AddBoth("/AddFile",AddFile);
AddBoth("/Progress",ProgressFunc);
AddBoth("/QueueList",QueueList);
AddBoth("/subscribe",Subscribe);
@@ -641,6 +766,28 @@ namespace Tesses.YouTubeDownloader.Server
public async Task ProgressFunc(ServerContext ctx)
{
await ctx.SendJsonAsync(Downloader.GetProgress());
+ }
+ public async Task AddFile(ServerContext ctx)
+ {
+ string url;
+ string downloadStr;
+ bool download=true;
+ if(ctx.QueryParams.TryGetFirst("url",out url))
+ {
+ if(ctx.QueryParams.TryGetFirst("download",out downloadStr))
+ {
+ bool dl;
+ if(bool.TryParse(downloadStr,out dl))
+ {
+ download=dl;
+ }
+ }
+
+ await Downloader.AddFileAsync(url,download);
+ await ctx.SendTextAsync(
+ $"You Will Be Redirected in 5 Sec
\n"
+ );
+ }
}
public async Task AddVideo(ServerContext ctx)
{
diff --git a/Tesses.YouTubeDownloader.Server/Tesses.YouTubeDownloader.Server.csproj b/Tesses.YouTubeDownloader.Server/Tesses.YouTubeDownloader.Server.csproj
index f2cbd3d..8ee06f7 100644
--- a/Tesses.YouTubeDownloader.Server/Tesses.YouTubeDownloader.Server.csproj
+++ b/Tesses.YouTubeDownloader.Server/Tesses.YouTubeDownloader.Server.csproj
@@ -5,7 +5,7 @@
-
+
@@ -15,9 +15,9 @@
Tesses.YouTubeDownloader.Server
Mike Nolan
Tesses
- 1.1.3
- 1.1.3
- 1.1.3
+ 1.1.4
+ 1.1.4
+ 1.1.4
Adds WebServer to TYTD
LGPL-3.0-only
true
diff --git a/Tesses.YouTubeDownloader/B64.cs b/Tesses.YouTubeDownloader/B64.cs
new file mode 100644
index 0000000..3887679
--- /dev/null
+++ b/Tesses.YouTubeDownloader/B64.cs
@@ -0,0 +1,58 @@
+using System;
+namespace Tesses.YouTubeDownloader
+{
+internal static class B64
+{
+ public static string Base64UrlEncodes(string arg)
+ {
+ return Base64UrlEncode(System.Text.Encoding.UTF8.GetBytes(arg));
+ }
+
+ public static string Base64Encode(byte[] arg)
+ {
+ return Convert.ToBase64String(arg);
+ }
+ public static byte[] Base64Decode(string arg)
+ {
+ return Convert.FromBase64String(arg);
+ }
+
+ public static string Base64Encodes(string arg)
+ {
+ return Base64Encode(System.Text.Encoding.UTF8.GetBytes(arg));
+ }
+
+ public static string Base64UrlEncode(byte[] arg)
+ {
+ string s = Convert.ToBase64String(arg); // Regular base64 encoder
+ s = s.Split('=')[0]; // Remove any trailing '='s
+ s = s.Replace('+', '-'); // 62nd char of encoding
+ s = s.Replace('/', '_'); // 63rd char of encoding
+ return s;
+ }
+ public static string Base64Decodes(string arg)
+ {
+ return System.Text.Encoding.UTF8.GetString(Base64Decode(arg));
+ }
+ public static string Base64UrlDecodes(string arg)
+ {
+ return System.Text.Encoding.UTF8.GetString(Base64UrlDecode(arg));
+ }
+ public static byte[] Base64UrlDecode(string arg)
+ {
+ string s = arg;
+ s = s.Replace('-', '+'); // 62nd char of encoding
+ s = s.Replace('_', '/'); // 63rd char of encoding
+ switch (s.Length % 4) // Pad with trailing '='s
+ {
+ case 0: break; // No pad chars in this case
+ case 2: s += "=="; break; // Two pad chars
+ case 3: s += "="; break; // One pad char
+ default: throw new System.Exception(
+ "Illegal base64url string!");
+ }
+ return Convert.FromBase64String(s); // Standard base64 decoder
+ }
+
+}
+}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader/BestStreams.cs b/Tesses.YouTubeDownloader/BestStreams.cs
index e40a42b..622ebd8 100644
--- a/Tesses.YouTubeDownloader/BestStreams.cs
+++ b/Tesses.YouTubeDownloader/BestStreams.cs
@@ -32,7 +32,7 @@ namespace Tesses.YouTubeDownloader
var f= await BestStreamInfo.GetBestStreams(storage,video.Id);
if(f ==null)
- return "";
+ return resolution == Resolution.NoDownload ? "" : resolution == Resolution.Mux ? $"Mux/{video.Id}.mkv" : $"{TYTDManager.ResolutionToDirectory(resolution)}/{video.Id}.mp4";
if(f.VideoFrozen)
{
@@ -61,13 +61,12 @@ namespace Tesses.YouTubeDownloader
public static async Task GetBestStreams(ITYTDBase storage,VideoId id)
{
//Console.WriteLine("IN FUNC");
- if(storage.DirectoryExists("StreamInfo"))
- {
+
//Console.WriteLine("DIR");
- if(storage.FileExists($"StreamInfo/{id.Value}.json"))
+ if(storage.BestStreamInfoExists(id))
{
//Console.WriteLine("STREAMS");
- BestStreamsSerialized serialization=JsonConvert.DeserializeObject(await storage.ReadAllTextAsync($"StreamInfo/{id.Value}.json"));
+ BestStreamsSerialized serialization=await storage.GetBestStreamInfoAsync(id);
BestStreams streams=new BestStreams();
streams.VideoOnlyStreamInfo = new BestStreamInfo(serialization.VideoOnly);
@@ -76,16 +75,16 @@ namespace Tesses.YouTubeDownloader
return streams;
}
- }
+
return null;
}
public static async Task GetBestStreams(IStorage storage,VideoId id,CancellationToken token=default(CancellationToken),bool expire_check=true)
{
- if(storage.DirectoryExists("StreamInfo"))
- {
- if(storage.FileExists($"StreamInfo/{id.Value}.json"))
+
+
+ if(storage.BestStreamInfoExists(id))
{
- BestStreamsSerialized serialization=JsonConvert.DeserializeObject(await storage.ReadAllTextAsync($"StreamInfo/{id.Value}.json"));
+ BestStreamsSerialized serialization=await storage.GetBestStreamInfoAsync(id);
if(DateTime.Now < serialization.Expires || !expire_check)
{
BestStreams streams=new BestStreams();
@@ -95,9 +94,7 @@ namespace Tesses.YouTubeDownloader
return streams;
}
}
- }else{
- storage.CreateDirectory("StreamInfo");
- }
+
DateTime expires=DateTime.Now.AddHours(6);
try{
if(storage.VideoInfoExists(id))
@@ -126,8 +123,7 @@ namespace Tesses.YouTubeDownloader
streams1.MuxedStreamInfo =new BestStreamInfo();
streams1.MuxedStreamInfo.SetInfo(muxed);
serialized.Muxed = streams1.MuxedStreamInfo.Serialization;
-
- await storage.WriteAllTextAsync($"StreamInfo/{id.Value}.json",JsonConvert.SerializeObject(serialized));
+ await storage.WriteBestStreamInfoAsync(id,serialized);
return streams1;
}catch(YoutubeExplodeException ex)
{
@@ -135,7 +131,7 @@ namespace Tesses.YouTubeDownloader
return null;
}
}
- private class BestStreamsSerialized
+ public class BestStreamsSerialized
{
public DateTime Expires {get;set;}
public BestStreamInfoSerialization VideoOnly {get;set;}
@@ -202,7 +198,7 @@ namespace Tesses.YouTubeDownloader
}
}
- internal class BestStreamInfoSerialization
+ public class BestStreamInfoSerialization
{
public string AudioCodec {get;set;}
public int FrameRate {get;set;}
diff --git a/Tesses.YouTubeDownloader/ConvertLegacy.cs b/Tesses.YouTubeDownloader/ConvertLegacy.cs
index 7e72345..0dcef97 100644
--- a/Tesses.YouTubeDownloader/ConvertLegacy.cs
+++ b/Tesses.YouTubeDownloader/ConvertLegacy.cs
@@ -133,7 +133,7 @@ namespace Tesses.YouTubeDownloader
int count = videos.Count;
foreach(var v in videos)
{
- await storage1.WriteAllTextAsync($"Info/{v.Id}.json",JsonConvert.SerializeObject(v));
+ await storage1.WriteVideoInfoAsync(v);
}
diff --git a/Tesses.YouTubeDownloader/DownloadLoop.cs b/Tesses.YouTubeDownloader/DownloadLoop.cs
index adc776d..c788861 100644
--- a/Tesses.YouTubeDownloader/DownloadLoop.cs
+++ b/Tesses.YouTubeDownloader/DownloadLoop.cs
@@ -112,6 +112,8 @@ namespace Tesses.YouTubeDownloader
}
+
+
public async Task GetSavedVideoAsync(VideoId id)
{
VideoMediaContext context=new VideoMediaContext(id,Resolution.PreMuxed);
@@ -128,6 +130,8 @@ namespace Tesses.YouTubeDownloader
private async Task DownloadVideoAsync(SavedVideo video, Resolution resolution, CancellationToken token=default(CancellationToken),IProgress progress=null,bool report=true)
{
try{
+ if(video.DownloadFrom == "YouTube")
+ {
switch (resolution)
{
case Resolution.Mux:
@@ -143,12 +147,103 @@ namespace Tesses.YouTubeDownloader
await DownloadVideoOnlyAsync(video,token,progress,report);
break;
}
+ }else if(video.DownloadFrom.StartsWith("NormalDownload,Length="))
+ {
+ await DownloadFileAsync(video,token,progress,report);
+ }
}catch(Exception ex)
{
- await GetLogger().WriteAsync(ex,video.Id);
+ VideoId? id=VideoId.TryParse(video.Id);
+ if(id.HasValue){
+ await GetLogger().WriteAsync(ex,id.Value);
+ }else{
+ await GetLogger().WriteAsync(ex);
+ }
}
}
+
+ private async Task DownloadFileAsync(SavedVideo video, CancellationToken token, IProgress progress, bool report)
+ {
+ string incomplete_file_path = $"Download/{B64.Base64UrlEncodes(video.Id)}-incomplete.part";
+ string file_path = $"Download/{B64.Base64UrlEncodes(video.Id)}.bin";
+ string url = video.Id;
+ bool canSeek=false;
+ long length=0;
+
+ foreach(var kvp in video.DownloadFrom.Split(',').Select>((e)=>{
+ if(!e.Contains('=')) return new KeyValuePair("","");
+ string[] keyVP = e.Split(new char[]{'='},2);
+
+
+ return new KeyValuePair(keyVP[0],keyVP[1]);}))
+ {
+ switch(kvp.Key)
+ {
+ case "CanSeek":
+ bool.TryParse(kvp.Value,out canSeek);
+ break;
+ case "Length":
+ long len;
+ if(long.TryParse(kvp.Value,out len))
+ {
+ length=len;
+ }
+ break;
+ }
+ }
+ await ReportStartVideo(video,Resolution.PreMuxed,length);
+ Func> openDownload = async(e)=>{
+ HttpRequestMessage msg=new HttpRequestMessage(HttpMethod.Get,url);
+ if(e > 0)
+ {
+ msg.Headers.Range.Ranges.Add(new System.Net.Http.Headers.RangeItemHeaderValue(e,null));
+ }
+
+ var res=await HttpClient.SendAsync(msg);
+ return await res.Content.ReadAsStreamAsync();
+ };
+
+ if(await Continue(file_path))
+ {
+ bool deleteAndRestart=false;
+
+ using(var file = await OpenOrCreateAsync(incomplete_file_path))
+ {
+ if(file.Length > 0 && !canSeek)
+ {
+ deleteAndRestart = true;
+ }
+ if(!deleteAndRestart)
+ {
+ Stream strm=await openDownload(file.Length);
+ bool res=await CopyStreamAsync(strm,file,0,length,4096,progress,token);
+ if(res)
+ {
+ RenameFile(incomplete_file_path,file_path);
+ if(report)
+ await ReportEndVideo(video, Resolution.PreMuxed);
+ }
+ }
+ }
+ if(deleteAndRestart){
+ DeleteFile(incomplete_file_path);
+ using(var file = await OpenOrCreateAsync(incomplete_file_path))
+ {
+ Stream strm=await openDownload(0);
+ bool res=await CopyStreamAsync(strm,file,0,length,4096,progress,token);
+ if(res)
+ {
+ RenameFile(incomplete_file_path,file_path);
+ if(report)
+ await ReportEndVideo(video, Resolution.PreMuxed);
+ }
+ }
+ }
+
+ }
+ }
+
public async Task Continue(string path)
{
@@ -282,7 +377,7 @@ namespace Tesses.YouTubeDownloader
}
curPos+=read;
await dest.WriteAsync(buffer,0,read);
- if(progress != null)
+ if(progress != null && len != 0)
{
progress.Report(curPos / len);
}
diff --git a/Tesses.YouTubeDownloader/IDownloader.cs b/Tesses.YouTubeDownloader/IDownloader.cs
index b095357..966aba6 100644
--- a/Tesses.YouTubeDownloader/IDownloader.cs
+++ b/Tesses.YouTubeDownloader/IDownloader.cs
@@ -17,7 +17,7 @@ namespace Tesses.YouTubeDownloader
Task AddChannelAsync(ChannelId id,Resolution resolution=Resolution.PreMuxed);
Task AddUserAsync(UserName userName,Resolution resolution=Resolution.PreMuxed);
-
+ Task AddFileAsync(string url,bool download=true);
IReadOnlyList<(SavedVideo Video,Resolution Resolution)> GetQueueList();
SavedVideoProgress GetProgress();
IAsyncEnumerable GetSubscriptionsAsync();
@@ -26,5 +26,6 @@ namespace Tesses.YouTubeDownloader
Task SubscribeAsync(UserName name,ChannelBellInfo info=ChannelBellInfo.NotifyAndDownload);
Task ResubscribeAsync(ChannelId id,ChannelBellInfo info=ChannelBellInfo.NotifyAndDownload);
void DeletePersonalPlaylist(string name);
+
}
}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader/IStorage.cs b/Tesses.YouTubeDownloader/IStorage.cs
index 79c688d..e56ac37 100644
--- a/Tesses.YouTubeDownloader/IStorage.cs
+++ b/Tesses.YouTubeDownloader/IStorage.cs
@@ -15,7 +15,7 @@ namespace Tesses.YouTubeDownloader
{
public interface IStorage : IWritable, IDownloader, ITYTDBase
{
-
+ Task WriteBestStreamInfoAsync(VideoId id,BestStreamInfo.BestStreamsSerialized serialized);
Task MuxVideosAsync(SavedVideo video,string videoSrc,string audioSrc,string videoDest,IProgress progress=null,CancellationToken token=default(CancellationToken));
Task Continue(string path);
Task WriteVideoInfoAsync(SavedVideo channel);
diff --git a/Tesses.YouTubeDownloader/ITYTDBase.cs b/Tesses.YouTubeDownloader/ITYTDBase.cs
index 890e622..3c9ae3a 100644
--- a/Tesses.YouTubeDownloader/ITYTDBase.cs
+++ b/Tesses.YouTubeDownloader/ITYTDBase.cs
@@ -15,7 +15,8 @@ namespace Tesses.YouTubeDownloader
{
public interface ITYTDBase : IPersonalPlaylistGet
{
-
+ Task GetBestStreamInfoAsync(VideoId id);
+ bool BestStreamInfoExists(VideoId id);
IAsyncEnumerable GetPersonalPlaylistsAsync();
Task<(String Path,bool Delete)> GetRealUrlOrPathAsync(string path);
diff --git a/Tesses.YouTubeDownloader/PreMediaContext.cs b/Tesses.YouTubeDownloader/PreMediaContext.cs
index d9db14b..9d50124 100644
--- a/Tesses.YouTubeDownloader/PreMediaContext.cs
+++ b/Tesses.YouTubeDownloader/PreMediaContext.cs
@@ -37,13 +37,13 @@ namespace Tesses.YouTubeDownloader
SavedChannel channel;
if(Id.HasValue) //dont check for if(Id != null) hince I was looking for several minutes for the bug
{
- string path=$"Channel/{Id.Value}.json";
- if(await storage.Continue(path))
+ //string path=$"Channel/{Id.Value}.json";
+ if(!storage.ChannelInfoExists(Id.Value))
{
try{
channel=await DownloadThumbnails(storage,await storage.YoutubeClient.Channels.GetAsync(Id.Value));
//channel=new SavedChannel(i);
- await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(channel));
+ await storage.WriteChannelInfoAsync(channel);
}catch(Exception ex)
{
await storage.GetLogger().WriteAsync(ex);
@@ -51,16 +51,16 @@ namespace Tesses.YouTubeDownloader
}
return channel;
}else{
- var j=JsonConvert.DeserializeObject(await storage.ReadAllTextAsync(path));
+ var j=await storage.GetChannelInfoAsync(Id.Value);
return j;
}
}else{
var c=await storage.YoutubeClient.Channels.GetByUserAsync(name1);
channel=await DownloadThumbnails(storage,c);
- string path=$"Channel/{c.Id.Value}.json";
- if(await storage.Continue(path))
+ //string path=$"Channel/{c.Id.Value}.json";
+ if(!storage.ChannelInfoExists(c.Id.Value))
{
- await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(channel));
+ await storage.WriteChannelInfoAsync(channel);
}
return channel;
@@ -118,7 +118,7 @@ namespace Tesses.YouTubeDownloader
public async Task FillQueue(TYTDStorage storage, List<(SavedVideo video, Resolution resolution)> Queue)
{
- string path=$"Playlist/{Id}.json";
+ // string path=$"Playlist/{Id}.json";
List videos=new List();
try{
@@ -134,7 +134,7 @@ namespace Tesses.YouTubeDownloader
await cmc.GetChannel(storage);
}
- await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(p));
+ await storage.WritePlaylistInfoAsync(p);
}catch(Exception ex)
{
await storage.GetLogger().WriteAsync(ex);
@@ -148,6 +148,76 @@ namespace Tesses.YouTubeDownloader
}
}
+ internal class NormalDownloadMediaContext : IMediaContext
+ {
+ public NormalDownloadMediaContext(string url,bool download=true)
+ {
+ this.url=url;
+ this.download=download;
+ }
+ bool download;
+ string url;
+ public async Task FillQueue(TYTDStorage storage, List<(SavedVideo video, Resolution resolution)> Queue)
+ {
+
+
+ SavedVideo video=new SavedVideo();
+ if(storage.DownloadExists(url)){
+ video = await storage.GetDownloadInfoAsync(url);
+ }else{
+ video.Id = url;
+
+ await GetFileNameAsync(storage,video);
+ }
+ lock(Queue){
+ Queue.Add((video,Resolution.PreMuxed));
+ }
+ }
+ private async Task GetFileNameAsync(TYTDStorage storage,SavedVideo video)
+ {
+ string[] uri0=url.Split(new char[]{'?'},2,StringSplitOptions.None);
+ string filename=Path.GetFileName(uri0[0]);
+ System.Net.Http.HttpRequestMessage message=new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Head,url);
+ message.Headers.Add("Range","bytes=0-");
+
+ var head=await storage.HttpClient.SendAsync(message);
+ if(head.Content.Headers.ContentDisposition != null && !string.IsNullOrWhiteSpace(head.Content.Headers.ContentDisposition.FileName))
+ {
+ filename = head.Content.Headers.ContentDisposition.FileName;
+ }
+ long length = 0;
+ if(head.Content.Headers.ContentLength.HasValue)
+ {
+ length = head.Content.Headers.ContentLength.Value;
+ }
+ video.Title = filename;
+ var res=head.StatusCode == System.Net.HttpStatusCode.PartialContent ? "true" : "false";
+ video.DownloadFrom=$"NormalDownload,Length={length},CanSeek={res}";
+ video.AuthorTitle = "NotYouTube";
+ video.AuthorChannelId = "TYTD_FILEDOWNLOAD";
+ List hdrs=new List();
+ foreach(var hdr in head.Content.Headers)
+ {
+ foreach(var item in hdr.Value){
+ hdrs.Add($"{hdr.Key}: {item}");
+ }
+ }
+ string headers=string.Join("\n",hdrs);
+ video.Description=$"File Download on \"{DateTime.Now.ToShortDateString()}\" at \"{DateTime.Now.ToShortTimeString()}\"\nHeaders:\n{headers}";
+ video.Likes=42;
+ video.Dislikes=42;
+ video.Views=42;
+ video.Duration = new TimeSpan(0,0,0);
+ video.Keywords = new string[] {"FILE"};
+ if(head.Headers.Date.HasValue)
+ {
+ video.UploadDate = head.Headers.Date.Value.DateTime;
+ }
+
+ await storage.WriteVideoInfoAsync(video);
+
+ }
+ }
internal class VideoMediaContext : IMediaContext
{
VideoId Id;
@@ -161,15 +231,15 @@ namespace Tesses.YouTubeDownloader
}
public async Task FillQueue(TYTDStorage storage,List<(SavedVideo,Resolution)> queue)
{
- string path=$"Info/{Id}.json";
+
SavedVideo video;
- if(await storage.Continue(path))
+ if(!storage.VideoInfoExists(Id))
{
try{
video = new SavedVideo(await storage.YoutubeClient.Videos.GetAsync(Id));
storage.SendBeforeSaveInfo(video);
- await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(video));
+ await storage.WriteVideoInfoAsync(video);
await video.DownloadThumbnails(storage);
}catch(Exception ex)
{
@@ -179,7 +249,7 @@ namespace Tesses.YouTubeDownloader
}
}else{
- video = JsonConvert.DeserializeObject(await storage.ReadAllTextAsync(path));
+ video = await storage.GetVideoInfoAsync(Id);
}
if(storage.GetLoggerProperties().AlwaysDownloadChannel)
{
diff --git a/Tesses.YouTubeDownloader/TYTD.cs b/Tesses.YouTubeDownloader/TYTD.cs
index 336ba48..9fb04ef 100644
--- a/Tesses.YouTubeDownloader/TYTD.cs
+++ b/Tesses.YouTubeDownloader/TYTD.cs
@@ -50,17 +50,23 @@ namespace Tesses.YouTubeDownloader
public abstract void MoveDirectory(string src,string dest);
public abstract void DeleteFile(string file);
public abstract void DeleteDirectory(string dir,bool recursive=false);
-
- public async Task WriteVideoInfoAsync(SavedVideo info)
+ public virtual async Task WriteBestStreamInfoAsync(VideoId id,BestStreamInfo.BestStreamsSerialized serialized)
{
- string file = $"Info/{info.Id}.json";
+ await WriteAllTextAsync($"StreamInfo/{id.Value}.json",JsonConvert.SerializeObject(serialized));
+
+ }
+
+ public virtual async Task WriteVideoInfoAsync(SavedVideo info)
+ {
+
+ string file = info.DownloadFrom.StartsWith("NormalDownload,Length=") ? $"FileInfo/{B64.Base64UrlEncodes(info.Id)}.json" : $"Info/{info.Id}.json";
if(!FileExists(file))
{
await WriteAllTextAsync(file,JsonConvert.SerializeObject(info));
}
}
- public async Task WritePlaylistInfoAsync(SavedPlaylist info)
+ public virtual async Task WritePlaylistInfoAsync(SavedPlaylist info)
{
string file = $"Playlist/{info.Id}.json";
if(!FileExists(file))
@@ -68,7 +74,7 @@ namespace Tesses.YouTubeDownloader
await WriteAllTextAsync(file,JsonConvert.SerializeObject(info));
}
}
- public async Task WriteChannelInfoAsync(SavedChannel info)
+ public virtual async Task WriteChannelInfoAsync(SavedChannel info)
{
string file = $"Channel/{info.Id}.json";
if(!FileExists(file))
@@ -111,6 +117,14 @@ namespace Tesses.YouTubeDownloader
}
await Task.FromResult(0);
}
+ public async Task AddFileAsync(string url,bool download=true)
+ {
+ lock(Temporary)
+ {
+ Temporary.Add(new NormalDownloadMediaContext(url,download));
+ }
+ await Task.FromResult(0);
+ }
public void CreateDirectoryIfNotExist(string dir)
{
if(!DirectoryExists(dir))
@@ -151,6 +165,9 @@ namespace Tesses.YouTubeDownloader
CreateDirectoryIfNotExist("Thumbnails");
CreateDirectoryIfNotExist("config");
CreateDirectoryIfNotExist("config/logs");
+ CreateDirectoryIfNotExist("FileInfo");
+ CreateDirectoryIfNotExist("Download");
+ CreateDirectoryIfNotExist("StreamInfo");
}
public void StartLoop(CancellationToken token = default(CancellationToken))
{
@@ -179,7 +196,7 @@ namespace Tesses.YouTubeDownloader
}
}
- public async Task AddToPersonalPlaylistAsync(string name, IEnumerable items)
+ public virtual async Task AddToPersonalPlaylistAsync(string name, IEnumerable items)
{
List items0=new List();
await foreach(var item in GetPersonalPlaylistContentsAsync(name))
@@ -191,18 +208,18 @@ namespace Tesses.YouTubeDownloader
}
- public async Task ReplacePersonalPlaylistAsync(string name, IEnumerable items)
+ public virtual async Task ReplacePersonalPlaylistAsync(string name, IEnumerable items)
{
await WriteAllTextAsync($"PersonalPlaylist/{name}.json",JsonConvert.SerializeObject(items.ToList()));
}
- public void DeletePersonalPlaylist(string name)
+ public virtual void DeletePersonalPlaylist(string name)
{
DeleteFile($"PersonalPlaylist/{name}.json");
}
- public async Task RemoveItemFromPersonalPlaylistAsync(string name, VideoId id)
+ public virtual async Task RemoveItemFromPersonalPlaylistAsync(string name, VideoId id)
{
List items0=new List();
await foreach(var item in GetPersonalPlaylistContentsAsync(name))
@@ -217,7 +234,7 @@ namespace Tesses.YouTubeDownloader
}
- public async Task SetResolutionForItemInPersonalPlaylistAsync(string name, VideoId id, Resolution resolution)
+ public virtual async Task SetResolutionForItemInPersonalPlaylistAsync(string name, VideoId id, Resolution resolution)
{
List items0=new List();
await foreach(var item in GetPersonalPlaylistContentsAsync(name))
diff --git a/Tesses.YouTubeDownloader/TYTDBase.cs b/Tesses.YouTubeDownloader/TYTDBase.cs
index a011c75..1487322 100644
--- a/Tesses.YouTubeDownloader/TYTDBase.cs
+++ b/Tesses.YouTubeDownloader/TYTDBase.cs
@@ -17,11 +17,11 @@ namespace Tesses.YouTubeDownloader
public abstract class TYTDBase : ITYTDBase
{
- public bool PersonalPlaylistExists(string name)
+ public virtual bool PersonalPlaylistExists(string name)
{
return FileExists($"PersonalPlaylist/{name}.json");
}
- public async IAsyncEnumerable GetPersonalPlaylistContentsAsync(string playlist)
+ public virtual async IAsyncEnumerable GetPersonalPlaylistContentsAsync(string playlist)
{
if(!PersonalPlaylistExists(playlist)) yield break;
var ls=JsonConvert.DeserializeObject>(await ReadAllTextAsync($"PersonalPlaylist/{playlist}.json"));
@@ -30,7 +30,7 @@ namespace Tesses.YouTubeDownloader
yield return await Task.FromResult(item);
}
}
- public async IAsyncEnumerable GetPersonalPlaylistsAsync()
+ public virtual async IAsyncEnumerable GetPersonalPlaylistsAsync()
{
await foreach(var item in EnumerateFilesAsync("PersonalPlaylist"))
{
@@ -62,7 +62,7 @@ namespace Tesses.YouTubeDownloader
return FileExistsAsync(path).GetAwaiter().GetResult();
}
- public async IAsyncEnumerable GetVideoIdsAsync()
+ public virtual async IAsyncEnumerable GetVideoIdsAsync()
{
await foreach(var item in EnumerateFilesAsync("Info"))
{
@@ -73,13 +73,13 @@ namespace Tesses.YouTubeDownloader
}
}
- public async Task GetVideoInfoAsync(VideoId id)
+ public virtual async Task GetVideoInfoAsync(VideoId id)
{
return JsonConvert.DeserializeObject(await ReadAllTextAsync($"Info/{id}.json"));
}
- public async IAsyncEnumerable GetVideosAsync()
+ public virtual async IAsyncEnumerable GetVideosAsync()
{
await foreach(var item in GetVideoIdsAsync())
{
@@ -90,7 +90,7 @@ namespace Tesses.YouTubeDownloader
}
}
}
- public async IAsyncEnumerable GetLegacyVideosAsync()
+ public virtual async IAsyncEnumerable GetLegacyVideosAsync()
{
await foreach(var item in GetVideoIdsAsync())
{
@@ -101,11 +101,11 @@ namespace Tesses.YouTubeDownloader
}
}
}
- public async Task GetLegacyVideoInfoAsync(VideoId id)
+ public virtual async Task GetLegacyVideoInfoAsync(VideoId id)
{
return JsonConvert.DeserializeObject(await ReadAllTextAsync($"Info/{id}.json"));
}
- public async IAsyncEnumerable GetPlaylistsAsync()
+ public virtual async IAsyncEnumerable GetPlaylistsAsync()
{
await foreach(var item in GetPlaylistIdsAsync())
{
@@ -131,7 +131,7 @@ namespace Tesses.YouTubeDownloader
}
}
- public async IAsyncEnumerable GetPlaylistIdsAsync()
+ public virtual async IAsyncEnumerable GetPlaylistIdsAsync()
{
await foreach(var item in EnumerateFilesAsync("Playlist"))
{
@@ -141,7 +141,7 @@ namespace Tesses.YouTubeDownloader
}
}
}
- public async IAsyncEnumerable GetChannelIdsAsync()
+ public virtual async IAsyncEnumerable GetChannelIdsAsync()
{
await foreach(var item in EnumerateFilesAsync("Channel"))
{
@@ -151,7 +151,7 @@ namespace Tesses.YouTubeDownloader
}
}
}
- public async IAsyncEnumerable GetYouTubeExplodeVideoIdsAsync()
+ public virtual async IAsyncEnumerable GetYouTubeExplodeVideoIdsAsync()
{
await foreach(var item in GetVideoIdsAsync())
{
@@ -159,11 +159,11 @@ namespace Tesses.YouTubeDownloader
if(id.HasValue) yield return id.Value;
}
}
- public async Task GetChannelInfoAsync(ChannelId id)
+ public virtual async Task GetChannelInfoAsync(ChannelId id)
{
return JsonConvert.DeserializeObject(await ReadAllTextAsync($"Channel/{id}.json"));
}
- public async IAsyncEnumerable GetChannelsAsync()
+ public virtual async IAsyncEnumerable GetChannelsAsync()
{
await foreach(var item in GetChannelIdsAsync())
{
@@ -175,24 +175,60 @@ namespace Tesses.YouTubeDownloader
}
}
- public bool PlaylistInfoExists(PlaylistId id)
+ public virtual async IAsyncEnumerable GetDownloadsAsync()
+ {
+ await foreach(var item in GetDownloadUrlsAsync())
+ {
+ yield return await GetDownloadInfoAsync(item);
+ }
+ }
+ public virtual async IAsyncEnumerable GetDownloadUrlsAsync()
+ {
+ await foreach(var item in EnumerateFilesAsync("FileInfo"))
+ {
+ if(Path.GetExtension(item).Equals(".json",StringComparison.Ordinal))
+ {
+ yield return B64.Base64UrlDecodes(Path.GetFileNameWithoutExtension(item));
+ }
+ }
+ }
+ public virtual async Task GetBestStreamInfoAsync(VideoId id)
+ {
+ return JsonConvert.DeserializeObject(await ReadAllTextAsync($"StreamInfo/{id.Value}.json"));
+ }
+ public virtual bool BestStreamInfoExists(VideoId id)
+ {
+ return FileExists($"StreamInfo/{id.Value}.json");
+ }
+ public virtual async Task GetDownloadInfoAsync(string url)
+ {
+ string enc=$"FileInfo/{B64.Base64UrlEncodes(url)}.json";
+ return JsonConvert.DeserializeObject(await ReadAllTextAsync(enc));
+
+ }
+ public virtual bool DownloadExists(string url)
+ {
+ string enc=$"FileInfo/{B64.Base64UrlEncodes(url)}.json";
+ return FileExists(enc);
+ }
+ public virtual bool PlaylistInfoExists(PlaylistId id)
{
return FileExists($"Playlist/{id}.json");
}
- public bool VideoInfoExists(VideoId id)
+ public virtual bool VideoInfoExists(VideoId id)
{
return FileExists($"Info/{id}.json");
}
- public bool ChannelInfoExists(ChannelId id)
+ public virtual bool ChannelInfoExists(ChannelId id)
{
return FileExists($"Channel/{id}.json");
}
- public async Task GetPlaylistInfoAsync(PlaylistId id)
+ public virtual async Task GetPlaylistInfoAsync(PlaylistId id)
{
return JsonConvert.DeserializeObject(await ReadAllTextAsync($"Playlist/{id}.json"));
}
- public async Task ReadAllTextAsync(string file)
+ public virtual async Task ReadAllTextAsync(string file)
{
using(var s = await OpenReadAsync(file))
{
@@ -203,12 +239,12 @@ namespace Tesses.YouTubeDownloader
}
}
- public bool DirectoryExists(string path)
+ public virtual bool DirectoryExists(string path)
{
return DirectoryExistsAsync(path).GetAwaiter().GetResult();
}
- public IEnumerable EnumerateFiles(string path)
+ public virtual IEnumerable EnumerateFiles(string path)
{
var e = EnumerateFilesAsync(path).GetAsyncEnumerator();
while(e.MoveNextAsync().GetAwaiter().GetResult())
@@ -216,7 +252,7 @@ namespace Tesses.YouTubeDownloader
yield return e.Current;
}
}
- public IEnumerable EnumerateDirectories(string path)
+ public virtual IEnumerable EnumerateDirectories(string path)
{
var e = EnumerateDirectoriesAsync(path).GetAsyncEnumerator();
while(e.MoveNextAsync().GetAwaiter().GetResult())
@@ -748,7 +784,7 @@ namespace Tesses.YouTubeDownloader
public interface IWritable : IPersonalPlaylistGet, IPersonalPlaylistSet
{
public Task WriteAllTextAsync(string path,string data);
- }
+ }
}
\ No newline at end of file
diff --git a/Tesses.YouTubeDownloader/TYTDClient.cs b/Tesses.YouTubeDownloader/TYTDClient.cs
index 2dcf0ca..1d7694a 100644
--- a/Tesses.YouTubeDownloader/TYTDClient.cs
+++ b/Tesses.YouTubeDownloader/TYTDClient.cs
@@ -256,7 +256,15 @@ internal class SegmentedHttpStream : Stream
_=ex;
}
}
-
+ public async Task AddFileAsync(string url,bool download=true)
+ {
+ try{
+ await client.GetStringAsync($"{url}api/v2/AddFile?url={WebUtility.UrlEncode(url)}&download={download}");
+ }catch(Exception ex)
+ {
+ _=ex;
+ }
+ }
public override async Task DirectoryExistsAsync(string path)
{
try{
diff --git a/Tesses.YouTubeDownloader/TYTDIDownloaderStorageProxy.cs b/Tesses.YouTubeDownloader/TYTDIDownloaderStorageProxy.cs
index f8a29d8..e8b91af 100644
--- a/Tesses.YouTubeDownloader/TYTDIDownloaderStorageProxy.cs
+++ b/Tesses.YouTubeDownloader/TYTDIDownloaderStorageProxy.cs
@@ -346,6 +346,11 @@ namespace Tesses.YouTubeDownloader
if(Downloader != null)
await Downloader.AddVideoAsync(id,resolution);
}
+ public async Task AddFileAsync(string url,bool download=true)
+ {
+ if(Downloader != null)
+ await Downloader.AddFileAsync(url,download);
+ }
public async Task AddPlaylistAsync(PlaylistId id, Resolution resolution = Resolution.PreMuxed)
{
@@ -660,6 +665,33 @@ namespace Tesses.YouTubeDownloader
e.DeletePersonalPlaylist(name);
});
}
+
+
+ public async Task WriteBestStreamInfoAsync(VideoId id, BestStreamInfo.BestStreamsSerialized serialized)
+ {
+ await StorageAsStorageAsync(async(e)=>{
+ await e.WriteBestStreamInfoAsync(id,serialized);
+ });
+ }
+
+ public async Task GetBestStreamInfoAsync(VideoId id)
+ {
+ BestStreamInfo.BestStreamsSerialized s=null;
+ await StorageAsStorageAsync(async(e)=>{
+ s=await e.GetBestStreamInfoAsync(id);
+ });
+
+ return s;
+ }
+
+ public bool BestStreamInfoExists(VideoId id)
+ {
+ bool res=false;
+ StorageAsStorage((e)=>{
+ res=e.BestStreamInfoExists(id);
+ });
+ return res;
+ }
}
public class DownloaderMigration
diff --git a/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj b/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj
index c8cc549..c62ed09 100644
--- a/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj
+++ b/Tesses.YouTubeDownloader/Tesses.YouTubeDownloader.csproj
@@ -7,9 +7,9 @@
Tesses.YouTubeDownloader
Mike Nolan
Tesses
- 1.1.5
- 1.1.5
- 1.1.5
+ 1.1.6
+ 1.1.6
+ 1.1.6
A YouTube Downloader
LGPL-3.0-only
true