using System.Linq; using SimpleHttp; using System; using System.Collections.Generic; 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; using Newtonsoft.Json; using System.Net; using System.Threading.Tasks; using youtube_downloader.Server.Models; namespace youtube_downloader { public class SSE { public static SSE ServerSentEventItem = new SSE(); List Streams = new List(); public void RegisterResponse(HttpListenerResponse rp) { Streams.Add(rp); } public async Task PushEventAsync(string event_name,object data) { await PushEventAsync(event_name, JsonConvert.SerializeObject(data)); } public async Task PushEventAsync(string event_name,string data) { await PushEventRawAsync($"event: {event_name}\ndata: {data}"); } public async Task PushEventRawAsync(string raw_data) { foreach (var items in Streams) { try { var bytes = Encoding.UTF8.GetBytes(raw_data + "\n\n"); await items.OutputStream.WriteAsync(bytes, 0, bytes.Length); await items.OutputStream.FlushAsync(); } catch (Exception ex) { _ = ex; } } } } class Program { static string webSitePath; static void Main(string[] arg) { Thread t = new Thread(new ThreadStart(() => { Server.Functions.Downloader.DL.DownloadThread().GetAwaiter().GetResult(); })); t.Start(); Thread t2 = new Thread(new ThreadStart(() => { Server.Functions.Downloader.DL.ListenForQueueItem().GetAwaiter().GetResult(); })); t2.Start(); // we need to get our app name so that // we can create unique names for our mutex and our pipe webSitePath = Server.Functions.Downloader.DL.GetPath(true, "WebSite"); Route.Before += Route_Before; /* Generic */ Route.Add("/api/AddItems", (HttpAction)AddItems, "POST"); Route.Add("/api/AddFile/{Url}", (HttpAction)AddFile); Route.Add("/api/AddCaptions/{Id}", (HttpAction)AddCaptions); /* Videos */ Route.Add("/api/AddVideoInfo/{Id}", (HttpAction)AddVideoInfo); Route.Add("/api/AddVideo/{Id}", (HttpAction)AddVideo); Route.Add("/api/AddVideoRes/{R}/{Id}", (HttpAction)AddVideoRes); Route.Add("/api/Redownload", (HttpAction)Redownload); Route.Add("/api/Redownload/{R}", (HttpAction)RedownloadRes); Route.Add("/api/Watch/{VideoId}", (HttpAction)Watch); /* Playlist */ Route.Add("/api/AddPlaylistOnly/{Id}", (HttpAction)AddPlaylistOnly); Route.Add("/api/AddPlaylist/{Id}", (HttpAction)AddPlaylist); Route.Add("/api/AddPlaylistRes/{R}/{Id}", (HttpAction)AddPlaylistRes); /* Search */ Route.Add("/api/SearchOnly/{text}", (HttpAction)SearchOnly); Route.Add("/api/Search/{text}", (HttpAction)Search); /* Channel */ Route.Add("/api/AddChannelOnly/{Id}", (HttpAction)AddChannelOnly); Route.Add("/api/AddChannel/{Id}", (HttpAction)AddChannel); Route.Add("/api/AddChannelRes/{R}/{Id}", (HttpAction)AddChannelRes); /* User */ Route.Add("/api/AddUserOnly/{Id}", (HttpAction)AddUserOnly); Route.Add("/api/AddUser/{Id}", (HttpAction)AddUser); Route.Add("/api/AddUserRes/{R}/{Id}", (HttpAction)AddUserRes); /* Queue and Progress */ Route.Add("/api/QueueList", (HttpAction)QueueList); Route.Add("/api/QueueMove/{From}/{To}", (HttpAction)QueueMove); Route.Add("/api/QueueMove2/{To}/{Id}", (HttpAction)QueueMove2); Route.Add("/api/Progress", (HttpAction)VideoProgress); Route.Add("/api/SSE", (HttpAction)ServerSentEvents); /* Storage */ Route.Add("/api/Storage/GetDirectories/{Path}", (HttpAction)StorageGetDirectories); Route.Add("/api/Storage/GetFiles/{Path}", (HttpAction)StorageGetFiles); Route.Add("/api/Storage/DirectoryExists/{Path}", (HttpAction)StorageDirectoryExists); Route.Add("/api/Storage/FileExists/{Path}", (HttpAction)StorageFileExists); Route.Add("/api/Storage/File/{Path}", (HttpAction)StorageFile); Route.Add("/api/Storage/Video/{Id}",(HttpAction)Video); Route.Add("/api/Storage/VideoRes/{Res}/{Id}",(HttpAction)VideoRes); Route.Add("/api/upload/", (HttpAction)UploadFiles, "POST"); /* Other */ Route.Add("/", (HttpAction)Index); Route.Add("/{Path}", (HttpAction)RootPath); Console.WriteLine("Almost Ready To Listen"); if (arg.Length > 0) { HttpServer.ListenAsync(arg[0], CancellationToken.None, Route.OnHttpRequestAsync).Wait(); } else { HttpServer.ListenAsync(3250, CancellationToken.None, Route.OnHttpRequestAsync).Wait(); } } #region Generic private static void AddItems(HttpListenerRequest request, HttpListenerResponse response, Dictionary arguments) { var f = request.ParseBody(arguments); foreach (var file in f.Values) { using (var req = new StreamReader(file.Value)) { List tripletlst = JsonConvert.DeserializeObject>(req.ReadToEnd()); Server.Functions.Downloader.DownloadItems(tripletlst); response.Redirect("/"); } } } public static void AddFile(HttpListenerRequest request, HttpListenerResponse response, Dictionary arguments) { Server.Functions.Downloader.DownloadFile(arguments["Url"]); } public static void AddCaptions(HttpListenerRequest request, HttpListenerResponse response, Dictionary arguments) { Server.Functions.Downloader.DownloadCaptions(arguments["Id"]); } #endregion #region Video public static void AddVideoInfo(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadVideoInfo(System.Web.HttpUtility.UrlDecode(args["Id"]), Resolution.NoConvert); rp.AsRedirect("/"); } public static void AddVideo(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadVideo(System.Web.HttpUtility.UrlDecode(args["Id"])); rp.AsRedirect("/"); } public static void AddVideoRes(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadVideo(System.Web.HttpUtility.UrlDecode(args["Id"]), (Resolution)int.Parse(args["R"])); rp.AsRedirect("/"); } public static void Redownload(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { foreach (var item in Directory.GetFiles(Server.Functions.Downloader.DL.GetPath(true, "Info"), "*.json")) { string id = System.IO.Path.GetFileNameWithoutExtension(item); Server.Functions.Downloader.DownloadVideo(id, Resolution.NoConvert); } rp.AsRedirect("/"); } public static void RedownloadRes(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { foreach (var item in Directory.GetFiles(Server.Functions.Downloader.DL.GetPath(true, "Info"), "*.json")) { string id = System.IO.Path.GetFileNameWithoutExtension(item); Server.Functions.Downloader.DownloadVideo(id, (Resolution)int.Parse(args["R"])); } rp.AsRedirect("/"); } public static void Watch(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { var txt = Templating.RenderFile(Path.Combine(webSitePath, "watch_page.thtml"), args); //populate template rp.AsText(txt); } #endregion #region Playlist public static void AddPlaylistOnly(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadPlaylistOnly(System.Web.HttpUtility.UrlDecode(args["Id"]), Resolution.NoConvert); rp.AsRedirect("/"); } public static void AddPlaylist(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadPlaylist(System.Web.HttpUtility.UrlDecode(args["Id"])); rp.AsRedirect("/"); } public static void AddPlaylistRes(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadPlaylist(System.Web.HttpUtility.UrlDecode(args["Id"]), (Resolution)int.Parse(args["R"])); rp.AsRedirect("/"); } #endregion #region Search public static void SearchOnly(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string search = System.Web.HttpUtility.UrlDecode(args["text"]); string json = JsonConvert.SerializeObject(Server.Functions.Downloader.Search(search, false)); rp.AsText(json, "application/json"); } public static void Search(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string search = System.Web.HttpUtility.UrlDecode(args["text"]); string json = JsonConvert.SerializeObject(Server.Functions.Downloader.Search(search)); rp.AsText(json, "application/json"); } #endregion #region Channel public static void AddChannelOnly(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadChannelOnly(System.Web.HttpUtility.UrlDecode(args["Id"])); rp.AsRedirect("/"); } public static void AddChannel(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadChannel(System.Web.HttpUtility.UrlDecode(args["Id"])); rp.AsRedirect("/"); } public static void AddChannelRes(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadChannel(System.Web.HttpUtility.UrlDecode(args["Id"]), (Resolution)int.Parse(args["R"])); rp.AsRedirect("/"); } #endregion #region User public static void AddUserOnly(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadUserOnly(System.Web.HttpUtility.UrlDecode(args["Id"])); rp.AsRedirect("/"); } public static void AddUser(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadUser(System.Web.HttpUtility.UrlDecode(args["Id"])); rp.AsRedirect("/"); } public static void AddUserRes(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.DownloadUser(System.Web.HttpUtility.UrlDecode(args["Id"]), (Resolution)int.Parse(args["R"])); rp.AsRedirect("/"); } #endregion #region Queue And Progress public static void QueueList(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string json = Server.Functions.Downloader.GetQueue(); rp.AsText(json, "application/json"); } public static void QueueMove(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { Server.Functions.Downloader.ModQueue(args["To"], args["From"]); rp.AsRedirect("/"); } public static void QueueMove2(HttpListenerRequest request, HttpListenerResponse response, Dictionary arguments) { Server.Functions.Downloader.ModQueue2(arguments["To"], arguments["Id"]); response.AsRedirect("/"); } public static void VideoProgress(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string json = JsonConvert.SerializeObject(Server.Functions.Downloader.GetProgress()); rp.AsText(json, "application/json"); } public static void ServerSentEvents(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { rp.AddHeader("Cache-Control", "no-cache"); rp.ContentType = "text/event-stream"; rp.WithCode(); Console.WriteLine(rp.SendChunked); rp.SendChunked = false; rp.ContentEncoding = Encoding.UTF8; SSE.ServerSentEventItem.RegisterResponse(rp); } #endregion #region Storage public static void StorageGetDirectories(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string path = Server.Functions.Downloader.DL.GetPath(true, args["Path"]); if (Directory.Exists(path)) { string json = Newtonsoft.Json.JsonConvert.SerializeObject(Directory.EnumerateDirectories(path).Select((o) => { return Path.GetFileName(o); })); rp.AsText(json, "application/json"); } else { rp.AsText("[]", "application/json"); } } public static void StorageGetFiles(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string path = Server.Functions.Downloader.DL.GetPath(true, args["Path"]); if (Directory.Exists(path)) { string json = Newtonsoft.Json.JsonConvert.SerializeObject(Directory.EnumerateFiles(path).Select((o) => { return Path.GetFileName(o); })); rp.AsText(json, "application/json"); } else { rp.AsText("[]", "application/json"); } } public static void StorageDirectoryExists(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string path = Server.Functions.Downloader.DL.GetPath(true, args["Path"]); string json = Directory.Exists(path) ? "true" : "false"; rp.AsText(json, "text/plain"); } public static void StorageFileExists(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string path = Server.Functions.Downloader.DL.GetPath(true, args["Path"]); string json = File.Exists(path) ? "true" : "false"; rp.AsText(json, "text/plain"); } public static void StorageFile(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { if (args["Path"].StartsWith("config/")) { rp.AsText("Access denied"); } else { string path = Server.Functions.Downloader.DL.GetPath(true, args["Path"]); rp.AsFile(rq, path); } } public static void Video(HttpListenerRequest rq,HttpListenerResponse rp,Dictionary args) { YoutubeExplode.Videos.VideoId? vid = YoutubeExplode.Videos.VideoId.TryParse(args["Id"]); if (vid.HasValue) { string path = Server.Functions.Downloader.DL.GetPath(true, "NotConverted",vid.Value +".mp4"); rp.AddHeader("Content-Disposition", GetVideoContentDisposition(vid.Value).ToString()); rp.AsFile(rq, path); } else { rp.WithCode(HttpStatusCode.BadRequest); rp.AsText("Invalid Video ID or URL", "text/plain"); } } public static void VideoRes(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { YoutubeExplode.Videos.VideoId? vid = YoutubeExplode.Videos.VideoId.TryParse(args["Id"]); if (vid.HasValue) { int res; if(int.TryParse(args["Res"],out res)) { if (res > 2 || res < 0) { rp.WithCode(HttpStatusCode.BadRequest); rp.AsText($"Invalid Resolution Number must be either 0, 1 or 2", "text/plain"); } else { string[] m = new string[] { "Converted", "NotConverted", "Audio" }; string path = Server.Functions.Downloader.DL.GetPath(true, m[res], vid.Value + ".mp4"); rp.AddHeader("Content-Disposition", GetVideoContentDisposition(vid.Value).ToString()); rp.AsFile(rq, path); } } else { rp.WithCode(HttpStatusCode.BadRequest); rp.AsText("Res is not a number", "text/plain"); } } else { rp.WithCode(HttpStatusCode.BadRequest); rp.AsText("Invalid Video ID or URL", "text/plain"); } } public static System.Net.Mime.ContentDisposition GetVideoContentDisposition(string id) { var cd = new System.Net.Mime.ContentDisposition(); string filename = GetVideoName(id); cd.FileName = filename; return cd; } public static string GetVideoName(string id) { string name = id + ".mp4"; string path = Server.Functions.Downloader.DL.GetPath(true, "Info", id + ".json"); if (File.Exists(path)) { string info=File.ReadAllText(path); name= JsonConvert.DeserializeObject(info).Title + ".mp4"; } string asAscii = Encoding.ASCII.GetString( Encoding.Convert( Encoding.UTF8, Encoding.GetEncoding( Encoding.ASCII.EncodingName, new EncoderReplacementFallback(string.Empty), new DecoderExceptionFallback() ), Encoding.UTF8.GetBytes(name) ) ); return asAscii; } public static void UploadFiles(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { var files = rq.ParseBody(args); foreach (var f in files.Values) f.Save(Path.Combine(webSitePath, f.FileName)); rp.AsText("uploaded", "text/plain"); } #endregion #region Other public static void Index(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { rp.AsFile(rq, Path.Combine(webSitePath, "index.html")); } public static void RootPath(HttpListenerRequest rq, HttpListenerResponse rp, Dictionary args) { string path = Path.Combine(webSitePath, args["Path"].Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0]); rp.AsFile(rq, path); } #endregion public static bool Route_Before(HttpListenerRequest request, HttpListenerResponse response) { response.WithCORS(); return false; } } }