2021-06-24 01:10:20 +00:00
|
|
|
|
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);
|
2021-06-24 01:49:05 +00:00
|
|
|
|
int sz = BitConverter.ToInt32(leng,0);
|
2021-06-24 01:10:20 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|