Added Subscriptions

This commit is contained in:
Mike Nolan 2022-05-09 17:00:19 -05:00
parent a87aad366b
commit ebcc4cda54
12 changed files with 439 additions and 59 deletions

View File

@ -10,7 +10,7 @@ namespace Tesses.YouTubeDownloader.Net6
TYTDCurrentDirectory currentDirectory=new TYTDCurrentDirectory(new HttpClient()); TYTDCurrentDirectory currentDirectory=new TYTDCurrentDirectory(new HttpClient());
TYTDServer server=new TYTDServer(currentDirectory); TYTDServer server=new TYTDServer(currentDirectory);
server.RootServer.Server=new StaticServer("WebSite"); server.RootServer.Server=new StaticServer("WebSite");
HttpServerListener listener=new HttpServerListener(server.InnerServer); HttpServerListener listener=new HttpServerListener(new System.Net.IPEndPoint(System.Net.IPAddress.Any,42440),server.InnerServer);
currentDirectory.StartLoop(); currentDirectory.StartLoop();
TYTDStorage.FFmpeg ="/usr/bin/ffmpeg"; TYTDStorage.FFmpeg ="/usr/bin/ffmpeg";
Console.WriteLine("Almost Ready to Listen"); Console.WriteLine("Almost Ready to Listen");

View File

@ -325,6 +325,7 @@ namespace Tesses.YouTubeDownloader.Server
public ApiV2Server(IDownloader downloader) public ApiV2Server(IDownloader downloader)
{ {
this.Downloader=downloader; this.Downloader=downloader;
Add("/AddItem",AddItem); Add("/AddItem",AddItem);
Add("/AddChannel",AddChannel); Add("/AddChannel",AddChannel);
Add("/AddUser",AddUser); Add("/AddUser",AddUser);
@ -332,7 +333,94 @@ namespace Tesses.YouTubeDownloader.Server
Add("/AddVideo",AddVideo); Add("/AddVideo",AddVideo);
Add("/Progress",ProgressFunc); Add("/Progress",ProgressFunc);
Add("/QueueList",QueueList); Add("/QueueList",QueueList);
Add("/subscribe",Subscribe);
Add("/resubscribe",Resubscribe);
} }
public async Task Resubscribe(ServerContext ctx)
{
TYTDStorage storage = Downloader as TYTDStorage;
if(storage != null)
{
string id;
if(ctx.QueryParams.TryGetFirst("id",out id))
{
string confstr;
ChannelBellInfo conf=ChannelBellInfo.NotifyAndDownload;
if(ctx.QueryParams.TryGetFirst("conf",out confstr))
{
if(!Enum.TryParse<ChannelBellInfo>(confstr,out conf))
{
conf = ChannelBellInfo.NotifyAndDownload;
}
}
ChannelId? cid=ChannelId.TryParse(WebUtility.UrlDecode(id));
if(cid.HasValue)
{
var sub=storage.GetSubscription(cid.Value);
if(sub != null)
{
sub.BellInfo = conf;
await storage.SaveSubscription(sub);
}
}
}
}
await ctx.SendTextAsync(
$"<html><head><title>You Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n"
);
}
public async Task Subscribe(ServerContext ctx)
{
TYTDStorage storage = Downloader as TYTDStorage;
if(storage != null)
{
string id;
if(ctx.QueryParams.TryGetFirst("id",out id))
{
string getinfostr;
bool getinfo=true;
if(ctx.QueryParams.TryGetFirst("getinfo",out getinfostr))
{
if(getinfostr == "false")
{
getinfo=false;
}
}
string confstr;
ChannelBellInfo conf=ChannelBellInfo.NotifyAndDownload;
if(ctx.QueryParams.TryGetFirst("conf",out confstr))
{
if(!Enum.TryParse<ChannelBellInfo>(confstr,out conf))
{
conf = ChannelBellInfo.NotifyAndDownload;
}
}
ChannelId? cid=ChannelId.TryParse(WebUtility.UrlDecode(id));
if(cid.HasValue)
{
await storage.Subscribe(cid.Value,getinfo,conf);
}else{
UserName? uname=UserName.TryParse(WebUtility.UrlDecode(id));
await storage.Subscribe(uname.Value,conf);
}
}
}
await ctx.SendTextAsync(
$"<html><head><title>You Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n"
);
}
public async Task QueueList(ServerContext ctx) public async Task QueueList(ServerContext ctx)
{ {
await ctx.SendJsonAsync(Downloader.GetQueueList()); await ctx.SendJsonAsync(Downloader.GetQueueList());
@ -362,7 +450,7 @@ namespace Tesses.YouTubeDownloader.Server
} }
} }
await ctx.SendTextAsync( await ctx.SendTextAsync(
$"<html><head><titleYou Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n" $"<html><head><title>You Will Be Redirected in 5 Sec</title><meta http-equiv=\"Refresh\" content=\"5; url='../../'\" /></head><body><h1>You Will Be Redirected in 5 Sec</h1></body></html>\n"
); );
} }
public async Task AddItem(ServerContext ctx) public async Task AddItem(ServerContext ctx)
@ -390,7 +478,7 @@ namespace Tesses.YouTubeDownloader.Server
public async Task AddUser(ServerContext ctx) public async Task AddUser(ServerContext ctx)
{ {
string id; string id;
if(ctx.QueryParams.TryGetFirst("v",out id)) if(ctx.QueryParams.TryGetFirst("id",out id))
{ {
Resolution resolution=Resolution.PreMuxed; Resolution resolution=Resolution.PreMuxed;
string res; string res;
@ -415,7 +503,7 @@ namespace Tesses.YouTubeDownloader.Server
public async Task AddChannel(ServerContext ctx) public async Task AddChannel(ServerContext ctx)
{ {
string id; string id;
if(ctx.QueryParams.TryGetFirst("v",out id)) if(ctx.QueryParams.TryGetFirst("id",out id))
{ {
Resolution resolution=Resolution.PreMuxed; Resolution resolution=Resolution.PreMuxed;
string res; string res;
@ -440,7 +528,7 @@ namespace Tesses.YouTubeDownloader.Server
public async Task AddPlaylist(ServerContext ctx) public async Task AddPlaylist(ServerContext ctx)
{ {
string id; string id;
if(ctx.QueryParams.TryGetFirst("v",out id)) if(ctx.QueryParams.TryGetFirst("id",out id))
{ {
Resolution resolution=Resolution.PreMuxed; Resolution resolution=Resolution.PreMuxed;
string res; string res;

View File

@ -15,9 +15,9 @@
<PackageId>Tesses.YouTubeDownloader.Server</PackageId> <PackageId>Tesses.YouTubeDownloader.Server</PackageId>
<Author>Mike Nolan</Author> <Author>Mike Nolan</Author>
<Company>Tesses</Company> <Company>Tesses</Company>
<Version>1.0.1.0</Version> <Version>1.0.2.0</Version>
<AssemblyVersion>1.0.1.0</AssemblyVersion> <AssemblyVersion>1.0.2.0</AssemblyVersion>
<FileVersion>1.0.1.0</FileVersion> <FileVersion>1.0.2.0</FileVersion>
<Description>Adds WebServer to TYTD</Description> <Description>Adds WebServer to TYTD</Description>
<PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression> <PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>

View File

@ -27,7 +27,11 @@ namespace Tesses.YouTubeDownloader
var (Video, Resolution) = Dequeue(out hasAny); var (Video, Resolution) = Dequeue(out hasAny);
if (hasAny) if (hasAny)
{ {
await DownloadVideoAsync(Video, Resolution, token,new Progress<double>(ReportProgress),true); await DownloadVideoAsync(Video, Resolution, token,new Progress<double>(
async (e)=>{
await ReportProgress(e);
}
),true);
} }
}catch(Exception ex) }catch(Exception ex)
@ -38,7 +42,7 @@ namespace Tesses.YouTubeDownloader
} }
public readonly SavedVideoProgress Progress = new SavedVideoProgress(); public readonly SavedVideoProgress Progress = new SavedVideoProgress();
private void ReportProgress(double progress) private async Task ReportProgress(double progress)
{ {
Progress.Progress = (int)(progress * 100); Progress.Progress = (int)(progress * 100);
Progress.ProgressRaw = progress; Progress.ProgressRaw = progress;
@ -47,11 +51,19 @@ namespace Tesses.YouTubeDownloader
{ {
foreach (var ext in ExtensionContext.Extensions) foreach (var ext in ExtensionContext.Extensions)
{ {
ext.VideoProgress(Progress.Video, progress); await ext.VideoProgress(Progress.Video, progress);
} }
} }
VideoProgressEventArgs args=new VideoProgressEventArgs();
args.VideoInfo=Progress.Video;
args.Progress=progress;
VideoProgress?.Invoke(this,args);
} }
private void ReportStartVideo(SavedVideo video, Resolution resolution, long length) private async Task ReportStartVideo(SavedVideo video, Resolution resolution, long length)
{ {
GetLogger().WriteAsync(video).Wait(); GetLogger().WriteAsync(video).Wait();
Progress.Video = video; Progress.Video = video;
@ -63,11 +75,17 @@ namespace Tesses.YouTubeDownloader
{ {
foreach (var item in ExtensionContext.Extensions) foreach (var item in ExtensionContext.Extensions)
{ {
item.VideoStarted(video, resolution, length); await item.VideoStarted(video, resolution, length);
} }
} }
VideoStartedEventArgs args=new VideoStartedEventArgs();
args.VideoInfo=video;
args.Resolution=resolution;
args.EstimatedLength=length;
VideoStarted?.Invoke(this,args);
} }
private void ReportEndVideo(SavedVideo video, Resolution resolution) private async Task ReportEndVideo(SavedVideo video, Resolution resolution)
{ {
Progress.Progress = 100; Progress.Progress = 100;
Progress.ProgressRaw = 1; Progress.ProgressRaw = 1;
@ -77,10 +95,15 @@ namespace Tesses.YouTubeDownloader
{ {
foreach (var item in ExtensionContext.Extensions) foreach (var item in ExtensionContext.Extensions)
{ {
item.VideoFinished(video, resolution); await item.VideoFinished(video, resolution);
} }
} }
VideoFinishedEventArgs args=new VideoFinishedEventArgs();
args.VideoInfo=video;
args.Resolution = resolution;
VideoFinished?.Invoke(this,args);
} }
public async Task DownloadNoQueue(SavedVideo info,Resolution resolution=Resolution.Mux,CancellationToken token=default(CancellationToken),IProgress<double> progress=null) public async Task DownloadNoQueue(SavedVideo info,Resolution resolution=Resolution.Mux,CancellationToken token=default(CancellationToken),IProgress<double> progress=null)
{ {
@ -399,7 +422,7 @@ namespace Tesses.YouTubeDownloader
return; return;
} }
if(report) if(report)
ReportStartVideo(video,Resolution.Mux,0); await ReportStartVideo(video,Resolution.Mux,0);
string complete = $"Muxed/{video.Id}.mkv"; string complete = $"Muxed/{video.Id}.mkv";
string incomplete = $"Muxed/{video.Id}incomplete.mkv"; string incomplete = $"Muxed/{video.Id}incomplete.mkv";
string complete_vidonly = $"VideoOnly/{video.Id}.{streams.VideoOnlyStreamInfo.Container}"; string complete_vidonly = $"VideoOnly/{video.Id}.{streams.VideoOnlyStreamInfo.Container}";
@ -414,7 +437,7 @@ namespace Tesses.YouTubeDownloader
} }
} }
if(report) if(report)
ReportEndVideo(video,Resolution.Mux); await ReportEndVideo(video,Resolution.Mux);
} }
private void DeleteIfExists(string path) private void DeleteIfExists(string path)
{ {
@ -447,7 +470,7 @@ namespace Tesses.YouTubeDownloader
return false; return false;
} }
if(report) if(report)
ReportStartVideo(video, Resolution.VideoOnly,streams.VideoOnlyStreamInfo.Size.Bytes); await ReportStartVideo(video, Resolution.VideoOnly,streams.VideoOnlyStreamInfo.Size.Bytes);
long len=await GetLengthAsync(incomplete); long len=await GetLengthAsync(incomplete);
using(var dest = await OpenOrCreateAsync(incomplete)) using(var dest = await OpenOrCreateAsync(incomplete))
@ -458,7 +481,7 @@ namespace Tesses.YouTubeDownloader
{ {
RenameFile(incomplete,complete); RenameFile(incomplete,complete);
if(report) if(report)
ReportEndVideo(video, Resolution.VideoOnly); await ReportEndVideo(video, Resolution.VideoOnly);
} }
} }
} }
@ -543,7 +566,7 @@ namespace Tesses.YouTubeDownloader
return false; return false;
} }
if(report) if(report)
ReportStartVideo(video, Resolution.AudioOnly,streams.AudioOnlyStreamInfo.Size.Bytes); await ReportStartVideo(video, Resolution.AudioOnly,streams.AudioOnlyStreamInfo.Size.Bytes);
long len=await GetLengthAsync(incomplete); long len=await GetLengthAsync(incomplete);
using(var dest = await OpenOrCreateAsync(incomplete)) using(var dest = await OpenOrCreateAsync(incomplete))
@ -554,7 +577,7 @@ namespace Tesses.YouTubeDownloader
{ {
RenameFile(incomplete,complete); RenameFile(incomplete,complete);
if(report) if(report)
ReportEndVideo(video, Resolution.AudioOnly); await ReportEndVideo(video, Resolution.AudioOnly);
} }
} }
} }
@ -591,7 +614,7 @@ namespace Tesses.YouTubeDownloader
return; return;
} }
if(report) if(report)
ReportStartVideo(video,Resolution.PreMuxed,streams.MuxedStreamInfo.Size.Bytes); await ReportStartVideo(video,Resolution.PreMuxed,streams.MuxedStreamInfo.Size.Bytes);
long len=await GetLengthAsync(incomplete); long len=await GetLengthAsync(incomplete);
bool ret; bool ret;
using(var dest = await OpenOrCreateAsync(incomplete)) using(var dest = await OpenOrCreateAsync(incomplete))
@ -603,7 +626,7 @@ namespace Tesses.YouTubeDownloader
{ {
RenameFile(incomplete,complete); RenameFile(incomplete,complete);
if(report) if(report)
ReportEndVideo(video, Resolution.PreMuxed); await ReportEndVideo(video, Resolution.PreMuxed);
} }
} }
} }

View File

@ -24,6 +24,36 @@ namespace Tesses.YouTubeDownloader
} }
public partial class TYTDStorage public partial class TYTDStorage
{ {
internal LoggerProperties Properties {get;set;}
public LoggerProperties GetProperties()
{
CreateDirectoryIfNotExist("config");
CreateDirectoryIfNotExist("config/logs");
if(FileExists("config/tytdprop.json"))
{
string data=ReadAllTextAsync("config/tytdprop.json").GetAwaiter().GetResult();
return JsonConvert.DeserializeObject<LoggerProperties>(data);
}else{
LoggerProperties prop=new LoggerProperties();
prop.AddDateInLog=true;
prop.LogVideoIds=true;
prop.PrintErrors =false;
prop.PrintVideoIds=true;
prop.UseLogs=true;
prop.SubscriptionInterval=TimeSpan.FromHours(1);
prop.AlwaysDownloadChannel = false;
return prop;
}
}
public LoggerProperties GetLoggerProperties()
{
if(Properties == null)
{
Properties=GetProperties();
}
return Properties;
}
internal static LockObj o=new LockObj(); internal static LockObj o=new LockObj();
private Logger _log=null; private Logger _log=null;
@ -41,6 +71,9 @@ namespace Tesses.YouTubeDownloader
} }
public class LoggerProperties public class LoggerProperties
{ {
public bool AlwaysDownloadChannel {get;set;}
public TimeSpan SubscriptionInterval {get;set;}
public bool UseLogs {get;set;} public bool UseLogs {get;set;}
public bool PrintVideoIds {get;set;} public bool PrintVideoIds {get;set;}
@ -58,29 +91,11 @@ namespace Tesses.YouTubeDownloader
private string _filename; private string _filename;
private TYTDStorage _storage; private TYTDStorage _storage;
public LoggerProperties Properties {get;set;}
private LoggerProperties GetProperties(TYTDStorage storage) internal Logger(TYTDStorage storage)
{ {
if(storage.FileExists("config/logger.json"))
{
string data=storage.ReadAllTextAsync("config/lggger.json").GetAwaiter().GetResult();
return JsonConvert.DeserializeObject<LoggerProperties>(data);
}else{
LoggerProperties prop=new LoggerProperties();
prop.AddDateInLog=true;
prop.LogVideoIds=true;
prop.PrintErrors =false;
prop.PrintVideoIds=true;
prop.UseLogs=true;
return prop;
}
}
internal Logger(TYTDStorage storage)
{
storage.CreateDirectoryIfNotExist("config");
storage.CreateDirectoryIfNotExist("config/logs");
Properties = GetProperties(storage);
_storage=storage; _storage=storage;
string dateTime = DateTime.Now.ToString("yyyyMMdd_hhmmss"); string dateTime = DateTime.Now.ToString("yyyyMMdd_hhmmss");
@ -110,11 +125,11 @@ namespace Tesses.YouTubeDownloader
{ {
if(writeToConsole) WriteStd(message,isError); if(writeToConsole) WriteStd(message,isError);
if(!log || !Properties.UseLogs) return; if(!log || !_storage.GetLoggerProperties().UseLogs) return;
// DateTime time = DateTime.Now; // DateTime time = DateTime.Now;
var msg= new StringBuilder(); var msg= new StringBuilder();
if(Properties.AddDateInLog) if(_storage.GetLoggerProperties().AddDateInLog)
{ {
var dat=DateTime.Now; var dat=DateTime.Now;
msg.AppendLine($"{dat.ToShortDateString()} at {dat.ToShortTimeString()}:"); msg.AppendLine($"{dat.ToShortDateString()} at {dat.ToShortTimeString()}:");
@ -142,11 +157,11 @@ namespace Tesses.YouTubeDownloader
public async Task WriteAsync(Exception ex) public async Task WriteAsync(Exception ex)
{ {
await WriteAsync($"Exception Catched:\n{ex.ToString()}",Properties.PrintErrors,true); await WriteAsync($"Exception Catched:\n{ex.ToString()}",_storage.GetLoggerProperties().PrintErrors,true);
} }
public async Task WriteAsync(SavedVideo video) public async Task WriteAsync(SavedVideo video)
{ {
await WriteAsync($"Download: {video.Title} with Id: {video.Id}",Properties.PrintVideoIds,false,Properties.LogVideoIds); await WriteAsync($"Download: {video.Title} with Id: {video.Id}",_storage.GetLoggerProperties().PrintVideoIds,false,_storage.GetLoggerProperties().LogVideoIds);
} }
} }

View File

@ -110,6 +110,7 @@ namespace Tesses.YouTubeDownloader
} }
public async Task FillQueue(TYTDStorage storage, List<(SavedVideo video, Resolution resolution)> Queue) public async Task FillQueue(TYTDStorage storage, List<(SavedVideo video, Resolution resolution)> Queue)
{ {
string path=$"Playlist/{Id}.json"; string path=$"Playlist/{Id}.json";
List<IVideo> videos=new List<IVideo>(); List<IVideo> videos=new List<IVideo>();
await foreach(var vid in storage.YoutubeClient.Playlists.GetVideosAsync(Id)) await foreach(var vid in storage.YoutubeClient.Playlists.GetVideosAsync(Id))
@ -117,6 +118,13 @@ namespace Tesses.YouTubeDownloader
videos.Add(vid); videos.Add(vid);
} }
var p=new SavedPlaylist(await storage.YoutubeClient.Playlists.GetAsync(Id),videos); var p=new SavedPlaylist(await storage.YoutubeClient.Playlists.GetAsync(Id),videos);
if(storage.GetLoggerProperties().AlwaysDownloadChannel)
{
var c=ChannelId.Parse(p.AuthorChannelId);
ChannelMediaContext cmc=new ChannelMediaContext(c,Resolution.NoDownload);
await cmc.GetChannel(storage);
}
await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(p)); await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(p));
if(Resolution == Resolution.NoDownload) return; if(Resolution == Resolution.NoDownload) return;
foreach(var item in videos) foreach(var item in videos)
@ -146,6 +154,8 @@ namespace Tesses.YouTubeDownloader
{ {
try{ try{
video = new SavedVideo(await storage.YoutubeClient.Videos.GetAsync(Id)); video = new SavedVideo(await storage.YoutubeClient.Videos.GetAsync(Id));
storage.SendBeforeSaveInfo(video);
await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(video)); await storage.WriteAllTextAsync(path,JsonConvert.SerializeObject(video));
await video.DownloadThumbnails(storage); await video.DownloadThumbnails(storage);
}catch(Exception ex) }catch(Exception ex)
@ -157,6 +167,13 @@ namespace Tesses.YouTubeDownloader
}else{ }else{
video = JsonConvert.DeserializeObject<SavedVideo>(await storage.ReadAllTextAsync(path)); video = JsonConvert.DeserializeObject<SavedVideo>(await storage.ReadAllTextAsync(path));
} }
if(storage.GetLoggerProperties().AlwaysDownloadChannel)
{
var c=ChannelId.Parse(video.AuthorChannelId);
ChannelMediaContext cmc=new ChannelMediaContext(c,Resolution.NoDownload);
await cmc.GetChannel(storage);
}
if(resolution == Resolution.NoDownload) return; if(resolution == Resolution.NoDownload) return;
lock(queue) lock(queue)
{ {

View File

@ -231,14 +231,14 @@ namespace Tesses.YouTubeDownloader
{ {
StringBuilder b=new StringBuilder(); StringBuilder b=new StringBuilder();
b.AppendLine($"Title: {Title}"); b.AppendLine($"Title: {Title}");
b.AppendLine($"💁 {AuthorTitle}"); b.AppendLine($"AuthorTitle: {AuthorTitle}");
DateTime date=UploadDate; DateTime date=UploadDate;
b.AppendLine($"📅 {date.ToShortDateString()}"); b.AppendLine($"Upload Date: {date.ToShortDateString()}");
b.AppendLine($"👍 {Likes}, 👎 {Dislikes}, 👁 {Views}"); b.AppendLine($"Likes: {Likes}, Dislikes: {Dislikes}, Views: {Views}");
b.AppendLine($"🕒 {Duration.ToString()}"); b.AppendLine($"Duration: {Duration.ToString()}");
b.AppendLine($"🔑 {string.Join(", ",Keywords)}"); b.AppendLine($"Tags: {string.Join(", ",Keywords)}");
b.AppendLine("Description:"); b.AppendLine("Description:");
b.AppendLine(Description); b.AppendLine(Description);
return b.ToString(); return b.ToString();

View File

@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.Xml;
using Newtonsoft.Json;
using YoutubeExplode.Channels;
using YoutubeExplode.Videos;
namespace Tesses.YouTubeDownloader
{
public class BellEventArgs : EventArgs
{
public VideoId Id {get;set;}
}
public class VideoProgressEventArgs : EventArgs
{
public SavedVideo VideoInfo {get;set;}
public double Progress {get;set;}
}
public class BeforeSaveInfoEventArgs : EventArgs
{
public SavedVideo VideoInfo {get;set;}
}
public class VideoStartedEventArgs : EventArgs
{
public SavedVideo VideoInfo {get;set;}
public Resolution Resolution {get;set;}
public long EstimatedLength {get;set;}
}
public class VideoFinishedEventArgs : EventArgs
{
public SavedVideo VideoInfo {get;set;}
public Resolution Resolution {get;set;}
}
public abstract partial class TYTDStorage
{
///<summary>
///For Notifications Like Bell on YouTube
///</summary>
public event EventHandler<BellEventArgs> Bell;
public event EventHandler<VideoStartedEventArgs> VideoStarted;
public event EventHandler<BeforeSaveInfoEventArgs> BeforeSaveInfo;
public event EventHandler<VideoProgressEventArgs> VideoProgress;
public event EventHandler<VideoFinishedEventArgs> VideoFinished;
internal void SendBeforeSaveInfo(SavedVideo video)
{
BeforeSaveInfo?.Invoke(this,new BeforeSaveInfoEventArgs() {VideoInfo=video});
}
internal void SendBell(VideoId i)
{
Bell?.Invoke(this,new BellEventArgs() {Id=i});
}
public DateTime LastSubscriptionTime = DateTime.MinValue;
public async Task HandleSubscriptions()
{
var date=DateTime.Now;
var interval=GetLoggerProperties().SubscriptionInterval;
if(date - interval < LastSubscriptionTime)
{
return;
}
foreach(var sub in Subscriptions)
{
await sub.LookOnYouTube();
}
LastSubscriptionTime=date;
}
public async IAsyncEnumerable<Subscription> GetSubscriptionsAsync()
{
await foreach(var item in EnumerateFilesAsync("Subscriptions"))
{
if(Path.GetExtension(item).Equals(".json"))
{
var sub=JsonConvert.DeserializeObject<Subscription>(item);
sub.Base=this;
yield return await Task.FromResult(sub);
}
}
}
/// <summary>
/// Subscribe to creator
/// </summary>
public async Task Subscribe(ChannelId id, bool downloadChannelInfo=false,ChannelBellInfo bellInfo = ChannelBellInfo.NotifyAndDownload)
{
if(downloadChannelInfo)
{
ChannelMediaContext context=new ChannelMediaContext(id,Resolution.NoDownload);
var c=await context.GetChannel(this);
}
Subscription sub=new Subscription();
sub.BellInfo = bellInfo;
sub.Base=this;
sub.Id=id.Value;
Subscriptions.Add(sub);
await SaveSubscription(sub);
}
public async Task SaveSubscription(Subscription sub)
{
await WriteAllTextAsync($"Subscriptions/{sub.Id}",JsonConvert.SerializeObject(sub));
}
public async Task Subscribe(UserName name,ChannelBellInfo bellInfo=ChannelBellInfo.NotifyAndDownload)
{
ChannelMediaContext context=new ChannelMediaContext(name,Resolution.NoDownload);
var c=await context.GetChannel(this);
await Subscribe(ChannelId.Parse(c.Id),false,bellInfo);
}
public void Unsubscribe(ChannelId id)
{
Subscription sub= Subscriptions.FirstOrDefault(e=>e.Id==id.Value);
if(sub != null)
{
Subscriptions.Remove(sub);
}
DeleteFile($"Subscriptions/{sub.Id}.json");
}
public Subscription GetSubscription(ChannelId id)
{
return Subscriptions.FirstOrDefault(e=>e.Id==id.Value);
}
}
[Flags]
public enum ChannelBellInfo
{
DoNothing=0,
GetInfo=1,
Notify=2,
Download=3,
NotifyAndDownload=Notify|Download
}
public class Subscription
{
[Newtonsoft.Json.JsonIgnore]
public TYTDStorage Base {get;set;}
[Newtonsoft.Json.JsonIgnore]
public DateTime LastCheckTime = DateTime.Now;
public string Id {get;set;}
public ChannelBellInfo BellInfo {get;set;}
public async Task<SavedChannel> GetChannelInfo()
{
if(await Base.FileExistsAsync($"Channel/{Id}.json"))
{
return await Base.GetChannelInfoAsync(Id);
}
return null;
}
public async Task LookOnYouTube()
{
try{
List<(string Id,DateTime Time)> items=new List<(string Id, DateTime Time)>();
XmlDocument doc=new XmlDocument();
doc.LoadXml(await Base.HttpClient.GetStringAsync($"https://www.youtube.com/feeds/videos.xml?channel_id={Id}"));
foreach(XmlNode item in doc.GetElementsByTagName("entry"))
{
string Id="";
DateTime Time=LastCheckTime-TimeSpan.FromMinutes(1);
foreach(XmlNode item2 in item.ChildNodes)
{
if(item2.Name == "yt:videoId")
{
if(BellInfo.HasFlag(ChannelBellInfo.Download))
{
await Base.AddVideoAsync(item2.InnerText);
}
Id=item2.InnerText;
}
if(item2.Name == "published")
{
Time = DateTime.Parse(item2.InnerText);
}
}
items.Add((Id,Time));
}
if(BellInfo.HasFlag(ChannelBellInfo.Notify) )
{
foreach(var item in items)
{
if(item.Time > LastCheckTime)
{
Base.SendBell(item.Id);
}
}
}
LastCheckTime=DateTime.Now;
}catch(Exception ex)
{
await Base.GetLogger().WriteAsync(ex);
}
}
}
}

View File

@ -115,6 +115,7 @@ namespace Tesses.YouTubeDownloader
} }
public void CreateDirectories() public void CreateDirectories()
{ {
CreateDirectoryIfNotExist("Subscriptions");
CreateDirectoryIfNotExist("VideoOnly"); CreateDirectoryIfNotExist("VideoOnly");
CreateDirectoryIfNotExist("AudioOnly"); CreateDirectoryIfNotExist("AudioOnly");
CreateDirectoryIfNotExist("Muxed"); CreateDirectoryIfNotExist("Muxed");

View File

@ -270,4 +270,4 @@ namespace Tesses.YouTubeDownloader
Directory.Delete(GetPath(dir),recursive); Directory.Delete(GetPath(dir),recursive);
} }
} }
} }

View File

@ -7,9 +7,9 @@
<PackageId>Tesses.YouTubeDownloader</PackageId> <PackageId>Tesses.YouTubeDownloader</PackageId>
<Author>Mike Nolan</Author> <Author>Mike Nolan</Author>
<Company>Tesses</Company> <Company>Tesses</Company>
<Version>1.0.2.4</Version> <Version>1.0.3.0</Version>
<AssemblyVersion>1.0.2.4</AssemblyVersion> <AssemblyVersion>1.0.3.0</AssemblyVersion>
<FileVersion>1.0.2.4</FileVersion> <FileVersion>1.0.3.0</FileVersion>
<Description>A YouTube Downloader</Description> <Description>A YouTube Downloader</Description>
<PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression> <PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>

View File

@ -32,13 +32,26 @@ namespace Tesses.YouTubeDownloader
{ {
return Progress; return Progress;
} }
List<Subscription> Subscriptions {get;set;}
List<(SavedVideo Video, Resolution Resolution)> QueueList = new List<(SavedVideo Video, Resolution Resolution)>(); List<(SavedVideo Video, Resolution Resolution)> QueueList = new List<(SavedVideo Video, Resolution Resolution)>();
List<IMediaContext> Temporary =new List<IMediaContext>(); List<IMediaContext> Temporary =new List<IMediaContext>();
private async Task QueueLoop(CancellationToken token) private async Task QueueLoop(CancellationToken token)
{ {
try{
Subscriptions=new List<Subscription>();
await foreach(var sub in GetSubscriptionsAsync())
{
Subscriptions.Add(sub);
}
}catch(Exception ex)
{
await GetLogger().WriteAsync(ex);
}
while(!token.IsCancellationRequested) while(!token.IsCancellationRequested)
{ try{ { try{
IMediaContext context; IMediaContext context;
lock(Temporary) lock(Temporary)
{ {