Add project files.

This commit is contained in:
Demetria Lovato 2021-06-23 18:10:20 -07:00
parent 26bf9e2749
commit eb31e8889b
10 changed files with 1038 additions and 0 deletions

320
Program.cs Normal file
View File

@ -0,0 +1,320 @@
using CommandLine;
using System;
using System.IO;
using System.IO.Pipes;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using TessesYoutubeDownloader.Server.Models;
namespace youtube_downloader
{
class Program
{
static void SendStringArray(System.IO.Stream s, string[] array)
{
s.Write(BitConverter.GetBytes(array.Length), 0, 4);
foreach (var item in array)
{
byte[] lenOfitem = BitConverter.GetBytes(item.Length);
byte[] argtext = System.Text.Encoding.UTF8.GetBytes(item);
s.Write(lenOfitem, 0, lenOfitem.Length);
s.Write(argtext, 0, argtext.Length);
}
}
static string[] GetStringArray(System.IO.Stream s)
{
byte[] items = new byte[4];
s.Read(items, 0, items.Length);
int items2 = BitConverter.ToInt32(items, 0);
string[] arraydata = new string[items2];
for (int i = 0; i < items2; i++)
{
s.Read(items, 0, 4);
int item3 = BitConverter.ToInt32(items, 0);
byte[] v = new byte[item3];
s.Read(v, 0, item3);
arraydata[i] = System.Text.Encoding.UTF8.GetString(v);
}
return arraydata;
}
static string _AppName =
Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().GetName().Name);
static void Main(string[] args)
{
// we need to get our app name so that
// we can create unique names for our mutex and our pipe
var notAlreadyRunning = true;
// wrap the meat of the application code with a named mutex so it runs only once
using (var mutex = new Mutex(true, _AppName + "Singleton", out notAlreadyRunning))
{
if (notAlreadyRunning)
{
// do additional work here, startup stuff
// Console.WriteLine("Running. Press any key to exit...");
// ...
// now process our initial main command line
_ProcessCommandLine(args);
// start the IPC sink.
var srv = new NamedPipeServerStream(_AppName + "IPC",
PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
// it's easier to use the AsyncCallback than it is to use Tasks here:
// this can't block, so some form of async is a must
srv.BeginWaitForConnection(new AsyncCallback(_ConnectionHandler), srv);
// block here until exit
Console.ReadKey();
// if this was a windows forms app you would put your
// "Applicantion.Run(new MyForm());" here
// finally, run any teardown code and exit
srv.Close();
}
else // another instance is running
{
// connect to the main app
var cli = new NamedPipeClientStream(".", _AppName + "IPC", PipeDirection.InOut);
cli.Connect();
SendStringArray(cli, args);
byte[] leng = new byte[4];
cli.Read(leng, 0, 4);
int sz = BitConverter.ToInt32(leng);
byte[] sdata = new byte[sz];
cli.Read(sdata, 0, sz);
string strdata = System.Text.Encoding.UTF8.GetString(sdata);
Console.Write(strdata);
// serialize and send the command line
cli.Close();
// and exit
}
}
}
static void _ConnectionHandler(IAsyncResult result)
{
var srv = result.AsyncState as NamedPipeServerStream;
srv.EndWaitForConnection(result);
// we're connected, now deserialize the incoming command line
var inargs = GetStringArray(srv);
r = new RESPONSE(srv);
// process incoming command line
_ProcessCommandLine(inargs);
r.SendResponse();
srv = new NamedPipeServerStream(_AppName + "IPC", PipeDirection.InOut,
1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
srv.BeginWaitForConnection(new AsyncCallback(_ConnectionHandler), srv);
}
[Verb("exit", HelpText = "Download Video")]
public class ExitApp
{
}
[Verb("video", true, HelpText = "Download Video")]
public class DownloadVideo
{
[Value(0, Required = true, HelpText = "The id or url of video")]
public string Id { get; set; }
[Option('f', "format", Default = Resolution.NoConvert, HelpText = "possible values are (NoConvert,Convert,Audio)")]
public Resolution Format { get; set; }
}
[Verb("playlist", HelpText = "Download entire Playlist")]
public class DownloadPlaylist
{
[Value(0, Required = true, HelpText = "The id or url of playlist")]
public string Id { get; set; }
[Option('f', "format", Default = Resolution.NoConvert, HelpText = "possible values are (NoConvert,Convert,Audio)")]
public Resolution Format { get; set; }
}
[Verb("channel", HelpText = "Download entire Channel (using channel id)")]
public class DownloadChannel
{
[Value(0, Required = true, HelpText = "The id or url of channel")]
public string Id { get; set; }
[Option('f', "format", Default = Resolution.NoConvert, HelpText = "possible values are (NoConvert,Convert,Audio)")]
public Resolution Format { get; set; }
}
[Verb("user", HelpText = "Download entire Channel (using username)")]
public class DownloadUser
{
[Value(0, Required = true, HelpText = "The name or url of the user")]
public string Id { get; set; }
[Option('f', "format", Default = Resolution.NoConvert, HelpText = "possible values are (NoConvert,Convert,Audio)")]
public Resolution Format { get; set; }
}
[Verb("move", HelpText = "Move item in queue")]
public class MoveQueue
{
[Option('d', "moveto", HelpText = "can be (up,down,top,bottom,remove)")]
public string MoveTo { get; set; }
[Option('s', "movefrom", HelpText = "Can be number (index) or \"last\"")]
public string MoveFrom { get; set; }
}
[Verb("info", HelpText = "Get info")]
public class Info
{
[Option('m', "machine", Default = false, HelpText = "In json")]
public bool ForMachine { get; set; }
[Option('p', "page", Required = true, HelpText = "can be (progress,queue,location)")]
public string Page { get; set; }
}
static RESPONSE r=null;
static void _ProcessCommandLine(string[] args)
{
CommandLine.Parser.Default.ParseArguments<DownloadVideo, DownloadPlaylist, DownloadChannel, DownloadUser, MoveQueue, Info, ExitApp>(args)
.MapResult(
(DownloadVideo opts) => RunDownloadVideo(opts),
(DownloadPlaylist opts) => RunDownloadPlaylist(opts),
(DownloadChannel opts) => RunDownloadChannel(opts),
(DownloadUser opts) => RunDownloadUser(opts),
(MoveQueue opts) => RunMoveQueue(opts),
(Info opts) => RunInfo(opts),
(ExitApp opts) => RunExitApp(opts),
errs => 1);
}
static void WriteVideoInfo(StringBuilder b,SavedVideo v,bool description)
{
//v.AuthorChannelId
//v.AuthorTitle
//v.Dislikes
//v.Duration
//v.Id
//v.Keywords
//v.Likes
//v.Title
//v.UploadDate
//v.Views
//v.Description
b.AppendLine($"Title: {v.Title}");
b.AppendLine($"AuthorName: {v.AuthorTitle}");
b.AppendLine($"AuthorID: {v.AuthorChannelId}");
b.AppendLine($"Duration: {TimeSpan.FromSeconds(v.Duration).ToString()}");
b.AppendLine($"Id: {v.Id}");
b.AppendLine($"UploadDate: {v.UploadDate}");
b.AppendLine($"Views: {v.Views}, Likes: {v.Likes}, Dislikes: {v.Dislikes}");
b.AppendLine("Keywords:");
foreach(var kw in v.Keywords)
{
b.AppendLine($"\t{kw}");
}
if (description)
{
b.AppendLine("Description:");
b.AppendLine(v.Description);
}
}
private static int RunInfo(Info opts)
{ if (r != null)
{
switch (opts.Page)
{
case "progress":
if (opts.ForMachine)
{
string jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(Server.Functions.Downloader.GetProgress());
r.SendResponse(jsonData);
}
else
{
var s=Server.Functions.Downloader.GetProgress();
StringBuilder sb = new StringBuilder();
sb.AppendLine("=======Progress=======");
sb.AppendLine($"Progress: {s.Progress}%");
sb.AppendLine($"Size: {Math.Round((double)s.Length / (double)(1000 * 1000), 2)} MB");
sb.AppendLine();
sb.AppendLine("=======Video Info=======");
WriteVideoInfo(sb, s.Saved, false);
r.SendResponse(sb.ToString());
}
break;
case "queue":
if (opts.ForMachine)
{
string jsonData = Server.Functions.Downloader.GetQueue();
r.SendResponse(jsonData);
}
else
{
var s = Server.Functions.Downloader.GetQueueItems();
StringBuilder sb = new StringBuilder();
foreach(var item in s)
{
WriteVideoInfo(sb, item.Video, false);
sb.AppendLine();
}
r.SendResponse(sb.ToString());
}
break;
case "location":
r.SendResponse(Server.Functions.Downloader.DL.StorageLocation);
break;
}
}
return 0;
}
private static int RunMoveQueue(MoveQueue opts)
{
Server.Functions.Downloader.ModQueue(opts.MoveTo, opts.MoveFrom);
return 0;
}
private static int RunDownloadUser(DownloadUser opts)
{
Server.Functions.Downloader.DownloadUser(opts.Id, opts.Format);
return 0;
}
private static int RunExitApp(ExitApp opts)
{
Environment.Exit(0);
return 0;
}
private static int RunDownloadVideo(DownloadVideo opts)
{
Server.Functions.Downloader.DownloadVideo(opts.Id, opts.Format);
return 0;
}
private static int RunDownloadPlaylist(DownloadPlaylist opts)
{
Server.Functions.Downloader.DownloadPlaylist(opts.Id, opts.Format);
return 0;
}
private static int RunDownloadChannel(DownloadChannel opts)
{
Server.Functions.Downloader.DownloadChannel(opts.Id, opts.Format);
return 0;
}
}
}

27
RESPONSE.cs Normal file
View File

@ -0,0 +1,27 @@
using System;
namespace youtube_downloader
{
internal class RESPONSE
{
public RESPONSE(System.IO.Stream strm)
{
this.strm = strm;
}
System.IO.Stream strm;
public void SendResponse(string response)
{
if(strm != null)
{
strm.Write(BitConverter.GetBytes(response.Length), 0, 4);
strm.Write(System.Text.Encoding.UTF8.GetBytes(response), 0, response.Length);
strm.Dispose();
strm = null;
}
}
public void SendResponse()
{
SendResponse("");
}
}
}

View File

@ -0,0 +1,412 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YoutubeExplode.Videos;
using YoutubeExplode;
using System.Net.Http;
using System.Net;
using System.IO;
using TessesYoutubeDownloader.Server.Models;
using Newtonsoft.Json;
using YoutubeExplode.Videos.Streams;
using YoutubeExplode.Channels;
using YoutubeExplode.Playlists;
namespace youtube_downloader.Server.Functions
{
public class Downloader
{
public static YoutubeClient CreateYoutubeClient()
{
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
Http = new HttpClient(handler);
return new YoutubeExplode.YoutubeClient(Http);
}
internal static VideoDownloadProgress GetProgress()
{
return P;
}
static HttpClient Http;
YoutubeClient ytc = CreateYoutubeClient();
static VideoDownloadProgress P=new VideoDownloadProgress();
Progress<double> DownloadP =new Progress<double>( (e)=> { P.Progress = (int)(e * 100.0); });
List<SavedVideoObject> Queue = new List<SavedVideoObject>();
internal static string GetQueue()
{
string q;
lock (DL.Queue)
{
q = JsonConvert.SerializeObject(DL.Queue.Select<SavedVideoObject, SavedVideo>(o => o.Video)) ;
}
return q;
}
internal static void ModQueue(string mvto, string index)
{
try
{
//?mv=up|down|top|bottom|remove,int&i=0,last
lock (DL.Queue)
{
int index2 = 0;
if (index == "last")
{
index2 = DL.Queue.Count - 1;
}
else
{
if (!int.TryParse(index, out index2))
{
index2 = 0;
}
}
if (index2 >= DL.Queue.Count)
{
index2 = DL.Queue.Count - 1;
}
if (mvto == "top")
{
var v = DL.Queue[index2];
DL.Queue.Remove(v);
DL.Queue.Insert(0,v);
}
else if (mvto == "bottom")
{
var v = DL.Queue[index2];
DL.Queue.Remove(v);
DL.Queue.Add(v);
}
else if (mvto == "remove")
{
var v = DL.Queue[index2];
DL.Queue.Remove(v);
}
else if (mvto == "up")
{
if (index2 > 0)
{
var v = DL.Queue[index2];
DL.Queue.Remove(v);
DL.Queue.Insert(index2 - 1, v);
}
}
else if (mvto == "down")
{
if (index2 < DL.Queue.Count - 1)
{
var v = DL.Queue[index2];
DL.Queue.Remove(v);
DL.Queue.Insert(index2 + 1, v);
}
}
else
{
int n1;
if (int.TryParse(mvto, out n1))
{
var v = DL.Queue[index2];
DL.Queue.Remove(v);
if (n1 > index2)
{
DL.Queue.Insert(n1 - 1, v);
}
else
{
DL.Queue.Insert(n1, v);
}
}
}
}
}catch(Exception ex)
{
_ = ex;
}
}
public bool Continue(string v)
{
if (File.Exists(v))
{
using(var f = File.OpenRead(v))
{
return f.Length == 0;
}
}
return true;
}
private string gStorageLocation()
{
if (System.IO.File.Exists("loc.txt"))
{
string loc=System.IO.File.ReadAllText("loc.txt");
try
{
System.IO.Directory.CreateDirectory(loc);
if (System.IO.Directory.Exists(loc))
{
return loc;
}
}catch(Exception ex)
{
}
}
return Environment.CurrentDirectory;
}
public string StorageLocation { get { return gStorageLocation(); } }
private async Task _DownloadVideo(VideoId videoId,Resolution res)
{ string infpath= GetPath(true, "Info", videoId + ".json");
SavedVideoObject sv;
bool exist = File.Exists(videoId);
if (exist)
{
sv = new SavedVideoObject();
sv.Resolution = res;
sv.Video = Newtonsoft.Json.JsonConvert.DeserializeObject<SavedVideo>(File.ReadAllText(infpath));
}
else
{
var vinfo = await ytc.Videos.GetAsync(videoId);
sv = SavedVideo.CreateFrom(res, vinfo, _DownloadThumbnail);
}
if (!exist)
{
File.WriteAllText(infpath, JsonConvert.SerializeObject(sv.Video));
}
lock (Queue)
{
Queue.Insert(0,sv); //new elements get added to begining
}
}
public async Task DownloadThread()
{
do
{
bool canDownload = false;
SavedVideoObject v;
lock (Queue)
{
canDownload= Queue.Count > 0;
if (canDownload)
{
v = Queue[0];
Queue.RemoveAt(0);
P.Saved = v.Video;
Console.WriteLine($"Download: {v.Video.Title}");
}
else
{
v = null;
}
}
if (canDownload)
{
switch (v.Resolution)
{
case Resolution.Convert:
string mypath = GetPath(true, "Converted", v.Video.Id + "-vidonly.bkp");
string mypathaudio = GetPath(true, "Audio", v.Video.Id + "incomplete.mp4");
string mypathCompleteAudio = GetPath(true, "Audio", v.Video.Id + ".mp4");
string mypathComplete = GetPath(true, "Converted", v.Video.Id + ".mp4");
string mypathIncompleteConverting = GetPath(true, "Converted", "conv.mkv");
if (Continue(mypathComplete))
{
var s3 = await ytc.Videos.Streams.GetManifestAsync(v.Video.Id);
var best2 = s3.GetAudioOnlyStreams().GetWithHighestBitrate();
var best = s3.GetVideoOnlyStreams().GetWithHighestVideoQuality();
P.Length = best.Size.Bytes + best2.Size.Bytes;
ProgressTwo p = new ProgressTwo(best.Size.Bytes, best2.Size.Bytes, DownloadP);
await ytc.Videos.Streams.DownloadAsync(best,mypath, p.Video);
IProgress<double> pv = p.Video;
pv.Report(1);
if (Continue(mypathCompleteAudio))
{
await ytc.Videos.Streams.DownloadAsync(best2, mypathaudio,p.Audio);
File.Move(mypathaudio, mypathCompleteAudio);
}
IProgress<double> pa = p.Video;
pa.Report(1);
ffmpeg.mux(mypath,mypathCompleteAudio,mypathIncompleteConverting);
File.Move(mypathIncompleteConverting, mypathComplete);
}
break;
case Resolution.NoConvert:
string mypath2 = GetPath(true, "NotConverted", v.Video.Id + "incomplete.mp4");
string mypath2Complete = GetPath(true, "NotConverted", v.Video.Id + ".mp4");
if (Continue(mypath2Complete))
{
var s = await ytc.Videos.Streams.GetManifestAsync(v.Video.Id);
var best = s.GetMuxedStreams().GetWithHighestVideoQuality();
P.Length = best.Size.Bytes;
await ytc.Videos.Streams.DownloadAsync(best, mypath2, DownloadP);
File.Move(mypath2, mypath2Complete);
}
break;
case Resolution.Audio:
string mypath3 = GetPath(true, "Audio", v.Video.Id + "incomplete.mp4");
string mypath3Complete = GetPath(true, "Audio", v.Video.Id + ".mp4");
if (Continue(mypath3Complete))
{
var s2 = await ytc.Videos.Streams.GetManifestAsync(v.Video.Id);
var best2 = s2.GetAudioOnlyStreams().GetWithHighestBitrate();
P.Length = best2.Size.Bytes;
await ytc.Videos.Streams.DownloadAsync(best2, mypath3, DownloadP);
File.Move(mypath3, mypath3Complete);
}
break;
}
}
System.Threading.Thread.Sleep(1);
}
while (true);
}
private void _DownloadThumbnail(int w,int h,string id,string tnail)
{
try
{
string p = GetPath(true,"Thumbnails", w.ToString() + 'x' + h.ToString(), id + ".jpg");
if (!File.Exists(p))
{
ffmpeg.download_thumbnail(tnail, p);
}
}
catch(Exception ex)
{
_ = ex;
}
}
internal static List<SavedVideoObject> GetQueueItems()
{
return DL.Queue;
}
private async Task _DownloadChannel(ChannelId id,Resolution res)
{
var c=await ytc.Channels.GetAsync(id);
SavedChannel c2 = SavedChannel.FromChannel(c, _DownloadThumbnail);
string infpath = GetPath(true, "Channel", id + ".json");
File.WriteAllText(infpath, JsonConvert.SerializeObject(c2));
var cvids = ytc.Channels.GetUploadsAsync(id).GetAsyncEnumerator();
while (await cvids.MoveNextAsync())
{
await _DownloadVideo(cvids.Current.Id, res);
}
}
private async Task _DownloadPlaylist(PlaylistId id,Resolution res)
{
SavedPlaylist pl =await SavedPlaylist.FromPlaylistId(res, ytc, id, DownloadVideo, _DownloadThumbnail);
string infpath = GetPath(true, "Playlist", id + ".json");
File.WriteAllText(infpath, JsonConvert.SerializeObject(pl));
}
private async Task _DownloadUser(UserName name, Resolution res)
{
var c = await ytc.Channels.GetByUserAsync(name);
SavedChannel c2 = SavedChannel.FromChannel(c, _DownloadThumbnail);
string infpath = GetPath(true, "Channels", c.Id + ".json");
File.WriteAllText(infpath, JsonConvert.SerializeObject(c2));
var cvids = ytc.Channels.GetUploadsAsync(c.Id).GetAsyncEnumerator();
while (await cvids.MoveNextAsync())
{
await _DownloadVideo(cvids.Current.Id, res);
}
}
public static void DownloadVideo(VideoId v,Resolution res)
{
DL._DownloadVideo(v, res).GetAwaiter().GetResult();
}
public static void DownloadVideo(VideoId v)
{
DownloadVideo(v, Resolution.NoConvert);
}
public static void DownloadPlaylist(PlaylistId id,Resolution res)
{
DL._DownloadPlaylist(id, res).GetAwaiter().GetResult();
}
public static void DownloadPlaylist(PlaylistId id)
{
DownloadPlaylist(id, Resolution.NoConvert);
}
public static void DownloadChannel(ChannelId id,Resolution res)
{
DL._DownloadChannel(id, res).GetAwaiter().GetResult();
}
public static void DownloadChannel(ChannelId id)
{
DownloadChannel(id, Resolution.NoConvert);
}
public static void DownloadUser(UserName name, Resolution res)
{
DL._DownloadUser(name, res).GetAwaiter().GetResult();
}
public static void DownloadUser(UserName name)
{
DownloadUser(name, Resolution.NoConvert);
}
public string GetPath(bool createParent,params string[] _path)
{
if (createParent)
{
string dir = GetPath(_path);
try
{
Directory.CreateDirectory(Path.GetDirectoryName(dir));
}catch(Exception ex)
{
_ = ex;
}
return dir;
}
else
{
return GetPath(_path);
}
}
private string GetPath(params string[] _path)
{
string[] array2 = new string[_path.Length + 1];
array2[0] = StorageLocation;
Array.Copy(_path, 0, array2, 1,_path.Length);
return System.IO.Path.Combine(array2);
}
public static Downloader DL = new Downloader();
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Diagnostics;
using System.IO;
namespace youtube_downloader.Server.Functions
{
internal class ffmpeg
{
internal static string FFMPEG = ffmpeg.get_ffmpeg();
private static string get_ffmpeg()
{
if (File.Exists("ffmpeg.txt"))
{
return File.ReadAllText("ffmpeg.txt");
}
return "ffmpeg";
}
internal static void mux(string mypath, string mypathCompleteAudio, string mypathIncompleteConverting)
{
using (var p = new Process())
{
p.StartInfo.FileName = FFMPEG;
p.StartInfo.Arguments = $"-i \"{mypath}\' -i \"{mypathCompleteAudio}\" -c copy -map 0:v -map 1:a \"{mypathIncompleteConverting}\'";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (p.Start())
{
p.WaitForExit();
}
}
}
internal static void download_thumbnail(string tnail, string p2)
{
using (var p = new Process())
{
p.StartInfo.FileName = FFMPEG;
p.StartInfo.Arguments = $"-i \"{tnail}\' \"{p2}\'";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (p.Start())
{
p.WaitForExit();
}
}
}
}
public class ProgressTwo
{
double vanda;
double v;
double a;
double vval=0;
double aval=0;
IProgress<double> report;
public ProgressTwo(double vsize,double asize,IProgress<double> p)
{
vanda = vsize + asize;
v = vsize / vanda; //v = 0.1 if vsize == 20 and vanda=200
a = asize / vanda;
Video = new Progress<double>(ProgressVideo);
Audio = new Progress<double>(ProgressAudio);
report = p;
}
private void ProgressAudio(double d)
{
aval = d * a;
Process();
}
private void ProgressVideo(double d)
{
vval = d * v;
Process();
}
private void Process()
{
report.Report(aval + vval);
}
public Progress<double> Video { get; set; }
public Progress<double> Audio { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TessesYoutubeDownloader.Server.Models
{
class SavedChannel
{
public static SavedChannel FromChannel(YoutubeExplode.Channels.Channel c, Action<int, int, string,string> downloadThumbnail)
{
SavedChannel sc = new SavedChannel();
sc.Id= c.Id;
sc.Title=c.Title;
sc.Url = c.Url;
foreach(var thumb in c.Thumbnails)
{
downloadThumbnail(thumb.Resolution.Width, thumb.Resolution.Height,c.Id, thumb.Url);
}
return sc;
}
public string Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TessesYoutubeDownloader.Server.Models
{
class SavedPlaylist
{
public static async Task<SavedPlaylist> FromPlaylistId(Resolution res,YoutubeExplode.YoutubeClient ytc,YoutubeExplode.Playlists.PlaylistId id,Action<YoutubeExplode.Videos.VideoId,Resolution> addToQueue, Action<int, int, string,string> downloadThumbnail)
{
SavedPlaylist pl2 = new SavedPlaylist();
pl2.Videos = new List<string>();
var pl=await ytc.Playlists.GetAsync(id);
var a = pl.Author;
pl2.AuthorChannelId = a.ChannelId;
pl2.AuthorTitle = a.Title;
pl2.Id= pl.Id;
pl2.Description = pl.Description;
pl2.Title = pl.Title;
foreach (var thumb in pl.Thumbnails)
{
downloadThumbnail(thumb.Resolution.Width, thumb.Resolution.Height,id, thumb.Url);
}
var plv = ytc.Playlists.GetVideosAsync(id).GetAsyncEnumerator();
while(await plv.MoveNextAsync())
{
addToQueue(plv.Current.Id,res);
pl2.Videos.Add(plv.Current.Id);
}
return pl2;
}
public List<string> Videos { get; set; }
public string AuthorTitle { get; set; }
public string AuthorChannelId { get; set; }
public string Id { get; set; }
public string Description { get; set; }
public string Title { get; set; }
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TessesYoutubeDownloader.Server.Models
{
public enum Resolution
{
Convert=0,
NoConvert=1,
Audio=2
}
public class SavedVideoObject
{
public SavedVideo Video { get; set; }
public Resolution Resolution { get; set; }
}
public class SavedVideo
{
public static SavedVideoObject CreateFrom(Resolution res,YoutubeExplode.Videos.Video v,Action<int,int,string,string> downloadThumbnail)
{
SavedVideo sv = new SavedVideo();
sv.Title = v.Title;
sv.UploadDate = v.UploadDate.ToString();
sv.Id = v.Id;
sv.AuthorChannelId = v.Author.ChannelId;
sv.AuthorTitle = v.Author.Title;
sv.Description = v.Description;
sv.Dislikes = v.Engagement.DislikeCount;
sv.Duration = v.Duration.GetValueOrDefault().TotalSeconds;
sv.Keywords = v.Keywords.ToArray();
sv.Likes = v.Engagement.LikeCount;
sv.Views = v.Engagement.ViewCount;
foreach(var thumb in v.Thumbnails)
{
downloadThumbnail(thumb.Resolution.Width, thumb.Resolution.Height,v.Id, thumb.Url);
}
SavedVideoObject obj = new SavedVideoObject();
obj.Video = sv;
obj.Resolution = res;
return obj;
}
public string Title { get; set; }
public string UploadDate { get; set; }
public string[] Keywords { get; set; }
public string Id { get; set; }
public string AuthorTitle { get; set; }
public string AuthorChannelId { get; set; }
public string Description { get; set; }
public double Duration { get; set; }
public long Views { get; set; }
public long Likes { get; set; }
public long Dislikes { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace TessesYoutubeDownloader.Server.Models
{
internal class VideoDownloadProgress
{
public Models.SavedVideo Saved { get; set; }
public long Length { get; set; }
public int Progress { get; set; }
public double ProgressRaw { get; set; }
}
}

15
youtube-downloader.csproj Normal file
View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>youtube_downloader</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="YoutubeExplode" Version="6.0.3" />
</ItemGroup>
</Project>

25
youtube-downloader.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31410.357
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "youtube-downloader", "youtube-downloader.csproj", "{CAC0629B-5729-49F2-9316-34CF990BB725}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CAC0629B-5729-49F2-9316-34CF990BB725}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CAC0629B-5729-49F2-9316-34CF990BB725}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAC0629B-5729-49F2-9316-34CF990BB725}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAC0629B-5729-49F2-9316-34CF990BB725}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {51396FBD-23AB-477B-ADE1-3922A998DA18}
EndGlobalSection
EndGlobal