using System.Text; using System.Web; using Tesses.WebServer; using YoutubeExplode; using YoutubeExplode.Search; using YoutubeExplode.Videos; using LiteDB; using Newtonsoft.Json; using System.Security.Cryptography; using YoutubeExplode.Videos.Streams; using System.Net.Mime; namespace TYTDThrowback; class Program { ILiteDatabase liteDatabase=new LiteDatabase("data/database.db"); ILiteCollection Videos => liteDatabase.GetCollection("videos"); public class VideoInfo { public VideoInfo() { } public VideoInfo(SavedVideo tytd) { VideoId = tytd.Id; VideoTitle = tytd.Title; ChannelId = tytd.AuthorId; ChannelTitle=tytd.AuthorTitle; Description = tytd.Description; Likes = tytd.Likes; Views = tytd.Views; } public VideoInfo(Video video) { VideoId = video.Id; VideoTitle = video.Title; ChannelId = video.Author.ChannelId; ChannelTitle = video.Author.ChannelTitle; Views = video.Engagement.ViewCount; Likes = video.Engagement.LikeCount; Duration = video.Duration ?? TimeSpan.Zero; UploadDate = video.UploadDate.DateTime; Description = video.Description; } public long Id {get;set;} public string VideoId {get;set;}=""; public string VideoTitle {get;set;}=""; public string ChannelId {get;set;}=""; public string ChannelTitle {get;set;}=""; public long Views {get;set;}=0; public long Likes {get;set;}=0; public string Description {get;set;}=""; public TimeSpan Duration {get;set;}=TimeSpan.Zero; public DateTime UploadDate {get;set;}=DateTime.Now; public DateTime Expires {get;set;}=DateTime.Now; public string Url {get;set;}=""; public long Bitrate {get;set;}=0; public long Size {get;set;}=0; public string Container {get;set;}=""; public void SetIStream(IStreamInfo info) { Bitrate=info.Bitrate.BitsPerSecond; Url = info.Url; Size = info.Size.Bytes; Container = info.Container.Name; } public IStreamInfo GetIStream() { return new SStreamInfo(Url,Bitrate,Size,Container); } } static readonly HttpClient http = new HttpClient(); YoutubeClient client=new YoutubeClient(http); string Downloader {get;set;}="https://tytd.site.tesses.net/"; bool AllowAddingTo {get;set;}=true; bool AllowDownloadingFrom {get;set;}=true; bool AllowDownloadingInfoFrom {get;set;}=false; static void Main(string[] args) { new Program().Run(); } void Run() { if(File.Exists("data/tytd.json")) { var res=JsonConvert.DeserializeObject(File.ReadAllText("data/tytd.json")); AllowDownloadingFrom = res?.AllowDownloadingFrom ?? true; AllowAddingTo = res?.AllowAddingTo ?? true; AllowDownloadingInfoFrom = res?.AllowDownloadingInfoFrom ?? false; Downloader = res?.Downloader ?? "https://tytd.site.tesses.net/"; } RouteServer routeServer = new RouteServer(new StaticServer("data/www")); routeServer.Add("/search",SearchAsync); routeServer.Add("/info", InfoAsync); routeServer.Add("/download",DownloadAsync); routeServer.StartServer(9900); } async Task StreamAsync(ServerContext ctx,bool play, VideoInfo video) { ContentDisposition disposition=new ContentDisposition(); disposition.FileName = $"{video.VideoTitle}-{video.VideoId}.{video.Container}"; disposition.Inline = play; //disposition. ctx.ResponseHeaders.Add("Content-Disposition",disposition.ToString()); await ctx.SendStreamAsync(await client.Videos.Streams.GetAsync(video.GetIStream()),HeyRed.Mime.MimeTypesMap.GetMimeType($"file.{video.Container}")); } async Task DownloadAsync(ServerContext ctx) { VideoId? vi=null; if(ctx.QueryParams.TryGetFirst("v", out var v) && (vi = VideoId.TryParse(v) ).HasValue) { bool play = ctx.QueryParams.ContainsKey("play"); var info=await EnsureVideoAsync(vi.Value); DateTime now = DateTime.Now; if(now"); string dfm = "./not_allowed_df.html"; string atm = "./not_allowed_at.html"; if(!string.IsNullOrWhiteSpace(Downloader) && AllowDownloadingFrom) { dfm = $"{Downloader.TrimEnd('/')}/api/Storage/Video/{video.VideoId}"; } if(!string.IsNullOrWhiteSpace(Downloader) && AllowAddingTo) { atm = $"{Downloader.TrimEnd('/')}/api/AddVideo/{video.VideoId}"; } await ctx.SendTextAsync((await File.ReadAllTextAsync("data/www/info.html")).Replace("$TITLE---429492492942",HttpUtility.HtmlEncode(video.VideoTitle)).Replace("$AUTHOR---429492492942",HttpUtility.HtmlEncode(video.ChannelTitle)).Replace("$UPLOAD---429492492942",$"{video.UploadDate.Month}/{video.UploadDate.Day}/{video.UploadDate.Year}").Replace("$LIKES---429492492942",video.Likes.ToString()).Replace("$VIEWS---429492492942",video.Views.ToString()).Replace("$DURATION---429492492942",video.Duration.ToString()).Replace("$WOY---429492492942",$"https://www.youtube.com/watch?v={video.VideoId}").Replace("$WFY---429492492942",$"./download?v={video.VideoId}&play").Replace("$DFY---429492492942",$"./download?v={video.VideoId}").Replace("$DFM---429492492942",dfm).Replace("$ATM---429492492942",atm).Replace("$DESC---429492492942",desc)); } } async Task SearchAsync(ServerContext ctx) { if(!ctx.QueryParams.TryGetFirst("q",out var query)) query="Trending"; StringBuilder b = new StringBuilder(); void AddLi(VideoInfo info) { b.Append($"
  • {HttpUtility.HtmlEncode(info.VideoTitle)}
    {HttpUtility.HtmlEncode(info.ChannelTitle)}{info.UploadDate.Month}/{info.UploadDate.Day}/{info.UploadDate.Year}
    V: {info.Views} L: {info.Likes}{info.Duration.ToString()}
  • "); } await foreach(var item in client.Search.GetResultBatchesAsync(query,YoutubeExplode.Search.SearchFilter.Video)) { foreach(var v in item.Items) { var v2 = v as VideoSearchResult; if(v2 != null) AddLi(await EnsureVideoAsync(v2.Id)); } break; } await ctx.SendTextAsync((await File.ReadAllTextAsync("data/www/search.html")).Replace("$QUERY---429492492942",HttpUtility.HtmlAttributeEncode(query)).Replace("$LI---429492492942",b.ToString())); } private async Task TYTDVideoInfo(VideoId id) { if(!AllowDownloadingInfoFrom) return null; if(string.IsNullOrWhiteSpace(Downloader)) return null; try{ string url = Downloader.TrimEnd('/'); if((await http.GetStringAsync($"{url}/api/Storage/FileExists/Info/{id.Value}.json")) == "false") return null; var res=JsonConvert.DeserializeObject(await http.GetStringAsync($"{url}/api/Storage/File/Info/{id.Value}.json")); if(res != null) { return new VideoInfo(res); } return null; } catch(Exception ex) { _=ex; return null; } } private async Task EnsureVideoAsync(VideoId v) { var vid = Videos.FindOne(e=>e.VideoId == v.Value); if(vid == null) { var res=await TYTDVideoInfo(v.Value); if(res != null) { Videos.Insert(res); return res; } try{ var _res=await client.Videos.GetAsync(v.Value); res=new VideoInfo(_res); Videos.Insert(res); return res; } catch(Exception ex) { _=ex; } vid=new VideoInfo(); } return vid; } } internal class SStreamInfo : IStreamInfo { private string url; private long bitrate; private long size; private string container; public SStreamInfo(string url, long bitrate, long size, string container) { this.url = url; this.bitrate = bitrate; this.size = size; this.container = container; } public string Url => url; public Container Container => new Container(container); public FileSize Size => new FileSize(size); public Bitrate Bitrate => new Bitrate(bitrate); } public class SavedVideo { public string Id {get;set;}=""; public string Title {get;set;}=""; public string AuthorId {get;set;}=""; public string AuthorTitle {get;set;}=""; public long Likes {get;set;}=0; public long Views {get;set;}=0; public TimeSpan Duration {get;set;}=TimeSpan.Zero; public DateTime UploadDate {get;set;}=DateTime.Now; public string Description {get;set;}=""; }