Version 1.0.1

This commit is contained in:
Michael Nolan 2022-04-09 19:18:45 -05:00
parent 7dfa5bdf5d
commit dadd15f7bc
6 changed files with 307 additions and 37 deletions

View File

@ -8,6 +8,8 @@ using YoutubeExplode.Videos;
using System.Linq;
using System.IO;
using System.Text;
using YoutubeExplode.Playlists;
using YoutubeExplode.Channels;
namespace Tesses.YouTubeDownloader.Server
{
@ -94,22 +96,149 @@ namespace Tesses.YouTubeDownloader.Server
);
return asAscii;
}
public override async Task GetAsync(ServerContext ctx)
{
string path=ctx.UrlAndQuery;
if(path.StartsWith("/File/"))
/*if(path.StartsWith("/File/NotConverted/"))
{
using(var s = await baseCtl.OpenReadAsyncWithLength( WebUtility.UrlDecode(path.Substring(6))))
// redirect to new
// /File/NotConverted/xxxxxxxxxxx.mp4
string idmp4=WebUtility.UrlDecode(path.Substring(19));
if(idmp4.Length == 15)
{
string id=Path.GetFileNameWithoutExtension(idmp4);
string path2 = $"Info/{id}.json";
if(!await baseCtl.FileExistsAsync(path2))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
} var data= await baseCtl.ReadAllTextAsync(path2);
var data2=JsonConvert.DeserializeObject<SavedVideo>(data);
var loc= await BestStreams.GetPathResolution(baseCtl,data2,Resolution.PreMuxed);
if(!await baseCtl.FileExistsAsync(loc))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
}
using(var s = await baseCtl.OpenReadAsyncWithLength(loc))
{
await ctx.SendStreamAsync(s,HeyRed.Mime.MimeTypesMap.GetMimeType(loc));
}
}
}
else if(path.StartsWith("/File/Converted/"))
{
// redirect to new
// /File/NotConverted/xxxxxxxxxxx.mp4
string idmp4=WebUtility.UrlDecode(path.Substring(16));
if(idmp4.Length == 15)
{
string id=Path.GetFileNameWithoutExtension(idmp4);
string path2 = $"Info/{id}.json";
if(!await baseCtl.FileExistsAsync(path2))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
} var data= await baseCtl.ReadAllTextAsync(path2);
var data2=JsonConvert.DeserializeObject<SavedVideo>(data);
var loc= await BestStreams.GetPathResolution(baseCtl,data2,Resolution.Mux);
if(!await baseCtl.FileExistsAsync(loc))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
}
using(var s = await baseCtl.OpenReadAsyncWithLength(loc))
{
await ctx.SendStreamAsync(s,HeyRed.Mime.MimeTypesMap.GetMimeType(loc));
}
}
}
else if(path.StartsWith("/File/Audio/"))
{
// redirect to new
// /File/NotConverted/xxxxxxxxxxx.mp4
string idmp4=WebUtility.UrlDecode(path.Substring(12));
if(idmp4.Length == 15)
{
string id=Path.GetFileNameWithoutExtension(idmp4);
string path2 = $"Info/{id}.json";
if(!await baseCtl.FileExistsAsync(path2))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
} var data= await baseCtl.ReadAllTextAsync(path2);
var data2=JsonConvert.DeserializeObject<SavedVideo>(data);
var loc= await BestStreams.GetPathResolution(baseCtl,data2,Resolution.AudioOnly);
if(!await baseCtl.FileExistsAsync(loc))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
}
using(var s = await baseCtl.OpenReadAsyncWithLength(loc))
{
await ctx.SendStreamAsync(s,HeyRed.Mime.MimeTypesMap.GetMimeType(loc));
}
}
}
else if(path.StartsWith("/File/Info/"))
{
string idjson=WebUtility.UrlDecode(path.Substring(11));
string path2 = $"Info/{idjson}";
if(!await baseCtl.FileExistsAsync(path2))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
}
var data= await baseCtl.ReadAllTextAsync(path2);
var data2=JsonConvert.DeserializeObject<SavedVideo>(data);
await ctx.SendJsonAsync(data2.ToLegacy());
}
else*/ if(path.StartsWith("/File/"))
{
string file=WebUtility.UrlDecode(path.Substring(6));
if(!await baseCtl.FileExistsAsync(file))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
}
using(var s = await baseCtl.OpenReadAsyncWithLength(file))
{
await ctx.SendStreamAsync(s);
}
}else if(path.StartsWith("/GetFiles/"))
}/*else if(path.StartsWith("/File-v2/"))
{
string file=WebUtility.UrlDecode(path.Substring(9));
if(!await baseCtl.FileExistsAsync(file))
{
await NotFoundServer.ServerNull.GetAsync(ctx);
return;
}
using(var s = await baseCtl.OpenReadAsyncWithLength(file))
{
await ctx.SendStreamAsync(s);
}
}*/
else if(path.StartsWith("/GetFiles/"))
{
await ctx.SendJsonAsync(baseCtl.EnumerateFiles( WebUtility.UrlDecode(path.Substring(10))).ToList());
}else if(path.StartsWith("/GetDirectories/"))
{
await ctx.SendJsonAsync(baseCtl.EnumerateDirectories( WebUtility.UrlDecode(path.Substring(16))).ToList());
}else if(path.StartsWith("/FileExists/"))
}else if(path.StartsWith("/FileExists-v2/"))
{
await ctx.SendTextAsync(baseCtl.FileExists(WebUtility.UrlDecode(path.Substring(15))) ? "true" : "false","text/plain");
}
else if(path.StartsWith("/FileExists/"))
{
await ctx.SendTextAsync(baseCtl.FileExists(WebUtility.UrlDecode(path.Substring(12))) ? "true" : "false","text/plain");
}else if(path.StartsWith("/DirectoryExists/"))
@ -132,7 +261,7 @@ namespace Tesses.YouTubeDownloader.Server
{
//Console.WriteLine("F is not null");
string filename = Path.GetFileName(path0);
string filename = $"{v.Title}-{Path.GetFileName(path0)}";
string header=GetVideoContentDisposition(filename).ToString();
ctx.ResponseHeaders.Add("Content-Disposition",header);
using(var strm = await baseCtl.OpenReadAsync(path0))
@ -172,7 +301,7 @@ namespace Tesses.YouTubeDownloader.Server
{
//Console.WriteLine("F is not null");
string filename = Path.GetFileName(path0);
string filename = $"{v.Title}-{Path.GetFileName(path0)}";
string header=GetVideoContentDisposition(filename).ToString();
ctx.ResponseHeaders.Add("Content-Disposition",header);
using(var strm = await baseCtl.OpenReadAsync(path0))
@ -197,6 +326,10 @@ namespace Tesses.YouTubeDownloader.Server
{
this.Downloader=downloader;
Add("/AddItem",AddItem);
Add("/AddChannel",AddChannel);
Add("/AddUser",AddUser);
Add("/AddPlaylist",AddPlaylist);
Add("/AddVideo",AddVideo);
Add("/Progress",ProgressFunc);
Add("/QueueList",QueueList);
}
@ -208,7 +341,7 @@ namespace Tesses.YouTubeDownloader.Server
{
await ctx.SendJsonAsync(Downloader.GetProgress());
}
public async Task AddItem(ServerContext ctx)
public async Task AddVideo(ServerContext ctx)
{
string id;
if(ctx.QueryParams.TryGetFirst("v",out id))
@ -225,7 +358,103 @@ namespace Tesses.YouTubeDownloader.Server
VideoId? id1=VideoId.TryParse(id);
if(id1.HasValue)
{
await Downloader.AddItemAsync(id1,resolution);
await Downloader.AddVideoAsync(id1.Value,resolution);
}
}
await ctx.SendTextAsync(
$"<html><head><titleYou Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n"
);
}
public async Task AddItem(ServerContext ctx)
{
string id;
if(ctx.QueryParams.TryGetFirst("v",out id))
{
Resolution resolution=Resolution.PreMuxed;
string res;
if(ctx.QueryParams.TryGetFirst("res",out res))
{
if(!Enum.TryParse<Resolution>(res,out resolution))
{
resolution=Resolution.PreMuxed;
}
}
await Downloader.AddItemAsync(id,resolution);
}
await ctx.SendTextAsync(
$"<html><head><titleYou Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n"
);
}
public async Task AddUser(ServerContext ctx)
{
string id;
if(ctx.QueryParams.TryGetFirst("v",out id))
{
Resolution resolution=Resolution.PreMuxed;
string res;
if(ctx.QueryParams.TryGetFirst("res",out res))
{
if(!Enum.TryParse<Resolution>(res,out resolution))
{
resolution=Resolution.PreMuxed;
}
}
UserName? id1=UserName.TryParse(id);
if(id1.HasValue)
{
await Downloader.AddUserAsync(id1.Value,resolution);
}
}
await ctx.SendTextAsync(
$"<html><head><titleYou Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n"
);
}
public async Task AddChannel(ServerContext ctx)
{
string id;
if(ctx.QueryParams.TryGetFirst("v",out id))
{
Resolution resolution=Resolution.PreMuxed;
string res;
if(ctx.QueryParams.TryGetFirst("res",out res))
{
if(!Enum.TryParse<Resolution>(res,out resolution))
{
resolution=Resolution.PreMuxed;
}
}
ChannelId? id1=ChannelId.TryParse(id);
if(id1.HasValue)
{
await Downloader.AddChannelAsync(id1.Value,resolution);
}
}
await ctx.SendTextAsync(
$"<html><head><titleYou Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n"
);
}
public async Task AddPlaylist(ServerContext ctx)
{
string id;
if(ctx.QueryParams.TryGetFirst("v",out id))
{
Resolution resolution=Resolution.PreMuxed;
string res;
if(ctx.QueryParams.TryGetFirst("res",out res))
{
if(!Enum.TryParse<Resolution>(res,out resolution))
{
resolution=Resolution.PreMuxed;
}
}
PlaylistId? id1=PlaylistId.TryParse(id);
if(id1.HasValue)
{
await Downloader.AddPlaylistAsync(id1.Value,resolution);
}
}
await ctx.SendTextAsync(

View File

@ -15,9 +15,9 @@
<PackageId>Tesses.YouTubeDownloader.Server</PackageId>
<Author>Mike Nolan</Author>
<Company>Tesses</Company>
<Version>1.0.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Version>1.0.1.0</Version>
<AssemblyVersion>1.0.1.0</AssemblyVersion>
<FileVersion>1.0.1.0</FileVersion>
<Description>Adds WebServer to TYTD</Description>
<PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>

View File

@ -241,6 +241,7 @@ namespace Tesses.YouTubeDownloader
audioInfo.AudioCodec=asi.AudioCodec;
AudioInfo = audioInfo;
}
_si=info;
//vsi.VideoCodec
}
}

View File

@ -26,7 +26,7 @@ namespace Tesses.YouTubeDownloader
var (Video, Resolution) = Dequeue(out hasAny);
if (hasAny)
{
await DownloadVideoAsync(Video, Resolution, token);
await DownloadVideoAsync(Video, Resolution, token,new Progress<double>(ReportProgress),true);
}
}
@ -75,21 +75,41 @@ namespace Tesses.YouTubeDownloader
}
}
}
private async Task DownloadVideoAsync(SavedVideo video, Resolution resolution, CancellationToken token)
public async Task DownloadNoQueue(SavedVideo info,Resolution resolution=Resolution.Mux,CancellationToken token=default(CancellationToken),IProgress<double> progress=null)
{
await DownloadVideoAsync(info,resolution,token,progress,false);
}
public async Task<SavedVideo> GetSavedVideoAsync(VideoId id)
{
VideoMediaContext context=new VideoMediaContext(id,Resolution.PreMuxed);
List<(SavedVideo Video,Resolution)> s=new List<(SavedVideo Video, Resolution)>();
await context.FillQueue(this,s);
if(s.Count> 0)
{
return s.First().Video;
}
return null;
}
private async Task DownloadVideoAsync(SavedVideo video, Resolution resolution, CancellationToken token=default(CancellationToken),IProgress<double> progress=null,bool report=true)
{
switch (resolution)
{
case Resolution.Mux:
await DownloadVideoMuxedAsync(video,token);
await DownloadVideoMuxedAsync(video,token,progress,report);
break;
case Resolution.PreMuxed:
await DownloadPreMuxedVideoAsync(video, token);
await DownloadPreMuxedVideoAsync(video, token,progress,report);
break;
case Resolution.AudioOnly:
await DownloadAudioOnlyAsync(video,token);
await DownloadAudioOnlyAsync(video,token,progress,report);
break;
case Resolution.VideoOnly:
await DownloadVideoOnlyAsync(video,token);
await DownloadVideoOnlyAsync(video,token,progress,report);
break;
}
@ -349,15 +369,15 @@ namespace Tesses.YouTubeDownloader
Directory.Delete("TYTD_TEMP",true);
return ret;
}
private async Task DownloadVideoMuxedAsync(SavedVideo video,CancellationToken token)
private async Task DownloadVideoMuxedAsync(SavedVideo video,CancellationToken token,IProgress<double> progress,bool report=true)
{
bool isValid=true;
isValid=await DownloadVideoOnlyAsync(video,token);
isValid=await DownloadVideoOnlyAsync(video,token,progress,report);
if(token.IsCancellationRequested || !isValid)
{
return;
}
isValid = await DownloadAudioOnlyAsync(video,token);
isValid = await DownloadAudioOnlyAsync(video,token,progress,report);
if(token.IsCancellationRequested || !isValid)
{
return;
@ -367,6 +387,7 @@ namespace Tesses.YouTubeDownloader
{
return;
}
if(report)
ReportStartVideo(video,Resolution.Mux,0);
string complete = $"Muxed/{video.Id}.mkv";
string incomplete = $"Muxed/{video.Id}incomplete.mkv";
@ -376,12 +397,12 @@ namespace Tesses.YouTubeDownloader
if(await Continue(complete))
{
if(await MuxVideosAsync(video,complete_vidonly,complete_audonly,incomplete,new Progress<double>(ReportProgress),token))
if(await MuxVideosAsync(video,complete_vidonly,complete_audonly,incomplete,progress,token))
{
RenameFile(incomplete,complete);
}
}
if(report)
ReportEndVideo(video,Resolution.Mux);
}
private void DeleteIfExists(string path)
@ -391,10 +412,12 @@ namespace Tesses.YouTubeDownloader
File.Delete(path);
}
}
public async Task<bool> DownloadVideoOnlyAsync(SavedVideo video,CancellationToken token)
public async Task<bool> DownloadVideoOnlyAsync(SavedVideo video,CancellationToken token,IProgress<double> progress,bool report=true)
{
bool ret=false;
var streams = await BestStreamInfo.GetBestStreams(this, video.Id, token, false);
if(!can_download) return false;
if(streams != null)
{
await MoveLegacyStreams(video,streams);
@ -412,16 +435,18 @@ namespace Tesses.YouTubeDownloader
{
return false;
}
if(report)
ReportStartVideo(video, Resolution.VideoOnly,streams.VideoOnlyStreamInfo.Size.Bytes);
long len=await GetLengthAsync(incomplete);
using(var dest = await OpenOrCreateAsync(incomplete))
{
ret=await CopyStreamAsync(strm,dest,len,streams.VideoOnlyStreamInfo.Size.Bytes,4096,new Progress<double>(ReportProgress),token);
ret=await CopyStreamAsync(strm,dest,len,streams.VideoOnlyStreamInfo.Size.Bytes,4096,progress,token);
}
if(ret)
{
RenameFile(incomplete,complete);
if(report)
ReportEndVideo(video, Resolution.VideoOnly);
}
}
@ -482,10 +507,12 @@ namespace Tesses.YouTubeDownloader
}
}
public async Task<bool> DownloadAudioOnlyAsync(SavedVideo video,CancellationToken token)
private async Task<bool> DownloadAudioOnlyAsync(SavedVideo video,CancellationToken token,IProgress<double> progress,bool report=true)
{
bool ret=false;
var streams = await BestStreamInfo.GetBestStreams(this, video.Id, token, false);
if(!can_download) return false;
if(streams != null)
{
string complete = $"AudioOnly/{video.Id}.{streams.AudioOnlyStreamInfo.Container}";
@ -504,16 +531,18 @@ namespace Tesses.YouTubeDownloader
{
return false;
}
if(report)
ReportStartVideo(video, Resolution.AudioOnly,streams.AudioOnlyStreamInfo.Size.Bytes);
long len=await GetLengthAsync(incomplete);
using(var dest = await OpenOrCreateAsync(incomplete))
{
ret=await CopyStreamAsync(strm,dest,len,streams.AudioOnlyStreamInfo.Size.Bytes,4096,new Progress<double>(ReportProgress),token);
ret=await CopyStreamAsync(strm,dest,len,streams.AudioOnlyStreamInfo.Size.Bytes,4096,progress,token);
}
if(ret)
{
RenameFile(incomplete,complete);
if(report)
ReportEndVideo(video, Resolution.AudioOnly);
}
}
@ -527,9 +556,10 @@ namespace Tesses.YouTubeDownloader
//We know its resolution
return ret;
}
private async Task DownloadPreMuxedVideoAsync(SavedVideo video, CancellationToken token)
private async Task DownloadPreMuxedVideoAsync(SavedVideo video, CancellationToken token,IProgress<double> progress,bool report=true)
{
var streams = await BestStreamInfo.GetBestStreams(this, video.Id, token, false);
if(!can_download) return;
if(streams != null)
{
await MoveLegacyStreams(video,streams);
@ -549,17 +579,19 @@ namespace Tesses.YouTubeDownloader
{
return;
}
if(report)
ReportStartVideo(video,Resolution.PreMuxed,streams.MuxedStreamInfo.Size.Bytes);
long len=await GetLengthAsync(incomplete);
bool ret;
using(var dest = await OpenOrCreateAsync(incomplete))
{
ret=await CopyStreamAsync(strm,dest,len,streams.MuxedStreamInfo.Size.Bytes,4096,new Progress<double>(ReportProgress),token);
ret=await CopyStreamAsync(strm,dest,len,streams.MuxedStreamInfo.Size.Bytes,4096,progress,token);
}
//We know its resolution
if(ret)
{
RenameFile(incomplete,complete);
if(report)
ReportEndVideo(video, Resolution.PreMuxed);
}
}

View File

@ -14,6 +14,7 @@ namespace Tesses.YouTubeDownloader
{
public abstract partial class TYTDStorage : TYTDBase, IWritable, IDownloader
{
private static readonly HttpClient _default = new HttpClient();
public abstract Task<Stream> CreateAsync(string path);
public abstract void CreateDirectory(string path);
@ -28,7 +29,7 @@ namespace Tesses.YouTubeDownloader
}
public TYTDStorage()
{
HttpClient=new HttpClient();
HttpClient=_default;
YoutubeClient=new YoutubeClient(HttpClient);
ExtensionContext=null;
}
@ -40,6 +41,8 @@ namespace Tesses.YouTubeDownloader
}
}
bool can_download=true;
public bool CanDownload {get {return can_download;} set {can_download=value;}}
public abstract void MoveDirectory(string src,string dest);
public abstract void DeleteFile(string file);
@ -92,6 +95,7 @@ namespace Tesses.YouTubeDownloader
}
public async Task DownloadThumbnails(VideoId id)
{
if(!can_download) return;
string Id=id.Value;
string[] res=new string[] {"default.jpg","sddefault.jpg","mqdefault.jpg","hqdefault.jpg","maxresdefault.jpg"};
CreateDirectoryIfNotExist($"Thumbnails/{Id}");
@ -109,7 +113,7 @@ namespace Tesses.YouTubeDownloader
}
}
}
public void StartLoop(CancellationToken token = default(CancellationToken))
public void CreateDirectories()
{
CreateDirectoryIfNotExist("VideoOnly");
CreateDirectoryIfNotExist("AudioOnly");
@ -117,6 +121,10 @@ namespace Tesses.YouTubeDownloader
CreateDirectoryIfNotExist("PreMuxed");
CreateDirectoryIfNotExist("Info");
CreateDirectoryIfNotExist("Thumbnails");
}
public void StartLoop(CancellationToken token = default(CancellationToken))
{
CreateDirectories();
Thread thread0=new Thread(()=>{
DownloadLoop(token).Wait();
});

View File

@ -7,9 +7,9 @@
<PackageId>Tesses.YouTubeDownloader</PackageId>
<Author>Mike Nolan</Author>
<Company>Tesses</Company>
<Version>1.0.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Version>1.0.1.0</Version>
<AssemblyVersion>1.0.1.0</AssemblyVersion>
<FileVersion>1.0.1.0</FileVersion>
<Description>A YouTube Downloader</Description>
<PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>