/*
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("VideosVideos
");
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("ErrorError
");
}
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();
}