Add project files.
This commit is contained in:
parent
26bf9e2749
commit
eb31e8889b
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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("");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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
|
Loading…
Reference in New Issue