/* A Program that splits videos between two servers (determines which server using keywords) to https://gitlab.tesses.net/tesses50/tytd Copyright (C) 2023 Tesses This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ namespace TYTDMultiDownloader; using Tesses.YouTubeDownloader; using Tesses.WebServer; using System.Threading.Tasks; using YoutubeExplode.Videos; using YoutubeExplode.Channels; using YoutubeExplode.Playlists; using System.Collections.Generic; using System.Text; using Newtonsoft.Json; class Program { public class Downloader : IDownloader { public List Videos {get;set;}=new List(); TYTDPathDirectory dir; TYTDClient nwii; TYTDClient nswitch; Func isWii; public Downloader(TYTDPathDirectory dir,TYTDClient nwii,TYTDClient nswitch,Func isWii) { this.dir = dir; this.nwii = nwii; this.nswitch = nswitch; this.isWii = isWii; } public event EventHandler? VideoStarted; public event EventHandler? VideoProgress; public event EventHandler? VideoFinished; public event EventHandler? Error; public event EventHandler? Bell; public async Task AddChannelAsync(ChannelId id, Resolution resolution = Resolution.PreMuxed) { if(!dir.ChannelInfoExists(id)) { var res=await dir.YoutubeClient.Channels.GetAsync(id); await dir.WriteChannelInfoAsync(new SavedChannel(res)); } await foreach(var video in dir.YoutubeClient.Channels.GetUploadsAsync(id)) { await AddVideoAsync(video.Id,resolution); } } public async Task AddFileAsync(string url, bool download = true) { await Task.FromResult(0); } public async Task AddHandleAsync(ChannelHandle handle, Resolution resolution = Resolution.PreMuxed) { var res=await dir.YoutubeClient.Channels.GetByHandleAsync(handle); await SaveChannelDataAsync(res); await AddChannelAsync(res.Id,resolution); } public async Task AddPlaylistAsync(PlaylistId id, Resolution resolution = Resolution.PreMuxed) { var res=await dir.YoutubeClient.Playlists.GetAsync(id); List videos=new List(); await foreach(var item in dir.YoutubeClient.Playlists.GetVideosAsync(id)) { videos.Add(item); await AddVideoAsync(item.Id,resolution); } var pl=new SavedPlaylist(res,videos); await dir.WritePlaylistInfoAsync(pl); } public async Task AddSlugAsync(ChannelSlug handle, Resolution resolution = Resolution.PreMuxed) { var res=await dir.YoutubeClient.Channels.GetBySlugAsync(handle); await SaveChannelDataAsync(res); await AddChannelAsync(res.Id,resolution); } private async Task SaveChannelDataAsync(Channel res) { if(!dir.ChannelInfoExists(res.Id)) { await dir.WriteChannelInfoAsync(new SavedChannel(res)); } } public async Task AddToPersonalPlaylistAsync(string name, IEnumerable items) { await Task.FromResult(0); } public async Task AddUserAsync(UserName userName, Resolution resolution = Resolution.PreMuxed) { var res=await dir.YoutubeClient.Channels.GetByUserAsync(userName); await SaveChannelDataAsync(res); await AddChannelAsync(res.Id,resolution); } public async Task AddVideoAsync(VideoId id, Resolution resolution = Resolution.PreMuxed) { SavedVideo video; if(!dir.VideoInfoExists(id)) { video=new SavedVideo(await dir.YoutubeClient.Videos.GetAsync(id)); await dir.WriteVideoInfoAsync(video); }else{ video = await dir.GetVideoInfoAsync(id); } Videos.Add(video); if(resolution != Resolution.NoDownload) { await (isWii(video) ? nwii : nswitch).AddVideoAsync(id,resolution); if(isWii(video)) { Console.WriteLine($"Wii Download: {video.Title} with Id: {video.Id}"); }else{ Console.WriteLine($"Switch Download: {video.Title} with Id: {video.Id}"); } } } public void CancelDownload(bool restart = false) { } public void DeletePersonalPlaylist(string name) { } public ExtraData GetExtraData() { return new ExtraData(); } public async IAsyncEnumerable GetPersonalPlaylistContentsAsync(string name) { await Task.FromResult(0); yield break; } public SavedVideoProgress GetProgress() { return new SavedVideoProgress(); } public IReadOnlyList<(SavedVideo Video, Resolution Resolution)> GetQueueList() { return new List<(SavedVideo Video,Resolution Resolution)>(); } public async IAsyncEnumerable GetSubscriptionsAsync() { await Task.FromResult(0); yield break; } public bool PersonalPlaylistExists(string name) { return false; } public async Task RemoveItemFromPersonalPlaylistAsync(string name, VideoId id) { await Task.FromResult(0); } public async Task ReplacePersonalPlaylistAsync(string name, IEnumerable items) { await Task.FromResult(0); } public async Task ResubscribeAsync(ChannelId id, ChannelBellInfo info = ChannelBellInfo.NotifyAndDownload) { await Task.FromResult(0); } public async Task SetResolutionForItemInPersonalPlaylistAsync(string name, VideoId id, Resolution resolution) { await Task.FromResult(0); } public async Task SubscribeAsync(ChannelId id, ChannelBellInfo bellInfo = ChannelBellInfo.NotifyAndDownload) { await Task.FromResult(0); } public async Task SubscribeAsync(UserName name, ChannelBellInfo info = ChannelBellInfo.NotifyAndDownload) { await Task.FromResult(0); } public async Task UnsubscribeAsync(ChannelId id) { await Task.FromResult(0); } } private static Config _get_cfg() { Directory.CreateDirectory("working/WebSite"); if(File.Exists("working/cfg.json")) { var cfg=JsonConvert.DeserializeObject(File.ReadAllText("working/cfg.json")); if(cfg != null) return cfg; } return new Config(); } public static Config cfg = _get_cfg(); public static TYTDPathDirectory dir= new TYTDPathDirectory("working"); public static TYTDClient nintendo_wii = new TYTDClient(cfg.Match); //change my url to your url for if video added contains keyword/keywords from working/wii.txt public static TYTDClient nintendo_switch = new TYTDClient(cfg.NoMatch); //change my url to your url for if video added does not contain keyword/keywords from working/wii.txt public static List wiiLines = new List(); public static bool WiiSwitchHandler(SavedVideo video) { StringBuilder b = new StringBuilder(); foreach(var item in video.Keywords) { b.Append(item.Replace(" ","").ToLower()); } b.Append(video.Title.Replace(" ","").ToLower()); b.Append(video.Description.Replace(" ","").Replace("\n","").Replace("\r","").Replace("\t","").ToLower()); b.Append(video.AuthorTitle.Replace(" ","").ToLower()); string b2 = b.ToString(); foreach(var item in wiiLines) { if(b2.Contains(item)) return true; } return false; } static void Main(string[] args) { foreach(var item in cfg.Matches) { wiiLines.Add(item.Replace(" ","").ToLower()); } Func dl = ()=>{ return new Downloader(dir,nintendo_wii,nintendo_switch,WiiSwitchHandler); }; dir.CreateDirectories(); dir.CanDownload = false; //we do not want this computer to download any videos or thumbnails StaticServer staticServer = new StaticServer("working/WebSite",true); staticServer.AllowUpload=false; MountableServer mountableServer = new MountableServer(staticServer); mountableServer.Mount("/api/",new AddServer(dl)); mountableServer.StartServer(cfg.Port); } public class AddServer : Server { public override async Task PostAsync(ServerContext ctx) { ctx.ParseBody(); var dl=dl0(); Resolution _dl2=Resolution.NoDownload; string _dl; if(ctx.QueryParams.TryGetFirst("dl",out _dl)) { if(_dl == "on") _dl2=Resolution.PreMuxed; } string videoLs; if(ctx.QueryParams.TryGetFirst("urls",out videoLs)) { string[] _videos=videoLs.Replace("\r",",").Replace("\n",",").Replace(" ",",").Split(",",StringSplitOptions.RemoveEmptyEntries); foreach(var item in _videos) { await dl.AddItemAsync(item,_dl2); } StringBuilder b = new StringBuilder("Videos

Videos

"); foreach(var items in dl.Videos) { var strm=await BestStreamInfo.GetBestStreams(dir,items.Id); string title = $"{items.Title}-{items.Id}.{strm.MuxedStreamInfo.Container.Name}"; string item = $"{System.Web.HttpUtility.HtmlEncode(title)}
"; b.Append(item); } b.Append(""); await ctx.SendTextAsync(b.ToString()); return; } await ctx.SendTextAsync("Error

Error

"); } Func dl0; public AddServer(Func dl) { this.dl0 = dl; // this._lock = _lock; } public bool IsPath(ServerContext ctx,string path,out string myArgument) { if(ctx.UrlPath.StartsWith(path)) { myArgument = ctx.UrlPath.Substring(path.Length); return true; } myArgument = ""; return false; } public override async Task GetAsync(ServerContext ctx) { var dl=dl0(); try{ string data; if(IsPath(ctx,"/AddItem/",out data)) { await dl.AddItemAsync(data); } if(IsPath(ctx,"/AddVideo/",out data)) { VideoId? videoId = VideoId.TryParse(data); if(videoId.HasValue) { await dl.AddVideoAsync(videoId.Value); } } if(IsPath(ctx,"/AddItemRes/",out data)) { string[] data2=data.Split('/',2,StringSplitOptions.RemoveEmptyEntries); if(data2.Length == 2) { await dl.AddItemAsync(data2[1]); } } if(IsPath(ctx,"/AddVideoRes/",out data)) { string[] data2=data.Split('/',2,StringSplitOptions.RemoveEmptyEntries); if(data2.Length == 2) { VideoId? videoId = VideoId.TryParse(data2[1]); if(videoId.HasValue) { await dl.AddVideoAsync(videoId.Value); } } } if(ctx.UrlPath == "/Stream") { //?dl=true string v; if(ctx.QueryParams.TryGetFirst("v",out v)) { VideoId? id = VideoId.TryParse(v); if(id.HasValue) { SavedVideo video; if(!dir.VideoInfoExists(id.Value)) { video = new SavedVideo(await dir.YoutubeClient.Videos.GetAsync(id.Value)); await dir.WriteVideoInfoAsync(video); }else{ video = await dir.GetVideoInfoAsync(id.Value); } var strm=await BestStreamInfo.GetBestStreams(dir,id.Value); string title = $"{video.Title}-{video.Id}.{strm.MuxedStreamInfo.Container.Name}"; System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition(); cd.FileName = title; ctx.ResponseHeaders.Add("Content-Disposition",cd.ToString()); await ctx.SendStreamAsync(await dir.YoutubeClient.Videos.Streams.GetAsync(strm.MuxedStreamInfo),HeyRed.Mime.MimeTypesMap.GetMimeType(title)); return; } } } await ctx.SendTextAsync("OK"); }catch(Exception ex) { await ctx.SendExceptionAsync(ex); } } } } internal class Config { public string Match {get;set;}="http://192.168.0.142:3252/"; public string NoMatch {get;set;}="http://192.168.0.112:3252/"; public int Port {get;set;}=3252; public List Matches {get;set;}=new List(); }