tytd-extensions/Autofetch YT/Class1.cs

269 lines
11 KiB
C#

using SimpleHttp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using TYTD;
using Newtonsoft.Json;
using System.IO;
using YoutubeExplode.Channels;
using Hyperlinq;
namespace Autofetch_YT
{
public class RSSState
{
public RSSState()
{
ReqDuration = new TimeSpan(0,30, 0);
}
public Dictionary<string,YoutubeRSS> VideoRSS { get; set; }
public TimeSpan ReqDuration { get; set; }
}
public class AutofetchYT : Api
{
RSSState state;
public override void OnStart()
{
Route.Add("/api/Extensions/AutofetchYT/", (HttpAction)Autofetch);
Route.Add("/api/Extensions/AutofetchYT/change", (HttpAction)AutofetchChange, "POST");
Load();
SetTimer(state.ReqDuration);
}
public override IEnumerable<KeyValuePair<string, string>> GetUrls()
{
yield return new KeyValuePair<string, string>("AutoFetch", "api/Extensions/AutofetchYT/");
}
protected override void TimerElapsed()
{
foreach(var item in state.VideoRSS)
{
item.Value.Scan();
}
}
public HElement GetSite()
{
return H.html(
H.head(
H.meta(e => e.charset("UTF8")),
H.meta(e => e.http_equiv("X-UA-Compatible").content("IE=edge")),
H.meta(e => e.name("viewport").content("width=device-width, initial-scale=1.0")),
H.title("Autofetch YT"),
H.link(e => e.href("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css").rel("stylesheet")),
H.script(e => e.src("https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js")),
H.script(e => e.src("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js")),
H.link(e => e.href("../../favicon.ico").rel("shortcut icon").type("image/x-icon").Custom("crossorigin","use-credentials"))
),
H.body(
H.div(e=>e.css("container"),
H.h1("Autofetch YT"),
H.ul(e=>e.css("nav nav-tabs"),
H.li(e=>e.css("active"),
H.a(e=>e.Custom("data-toggle","tab").href("#addchannel"),"Add")
),
H.li(
H.a(e=> e.Custom("data-toggle", "tab").href("#channels"),"Channels")
),
H.li(
H.a(e => e.Custom("data-toggle", "tab").href("#more"), "More")
)
),
H.div(e=>e.css("tab-content"),
H.div(e=>e.id("addchannel").css("tab-pane fad in active"),
H.br(),
H.div(e=>e.css("container"),
H.form(e=>e.action("change").method("POST"),
H.input(e=>e.type("hidden").name("operation").value("add")),
H.input(e=>e.type("text").placeholder("Name").name("key")),
H.br(),H.br(),
H.input(e=>e.type("text").placeholder("Channel ID or URL").name("value")),
H.br(),H.br(),
H.input(e=>e.type("submit").value("Add Channel").css("btn btn-primary"))
)
)
),
H.div(e=>e.id("channels").css("tab-pane fade"),
H.br(),
H.div(e=>e.css("container"),
H.table(e=>e.css("table table-striped"),
H.thead(
H.tr(
H.th("Name"),
H.th("Channel Url"),
H.th("Delete")
)
),
H.tbody(
from item in state.VideoRSS
select RSSTr(item)
)
)
)
),
H.div(e => e.id("more").css("tab-pane fade"),
H.br(),
H.div(e => e.css("container"),
H.form(e=>e.action("change").method("POST"),
H.h1("Clear All Channels"),
H.input(e=>e.type("hidden").name("operation").value("clear")),
H.input(e=>e.type("submit").value("Clear").css("btn btn-danger"))
),
H.form(e => e.action("change").method("POST"),
H.h1("Interval"),
H.input(e=>e.type("hidden").name("operation").value("setinterval")),
H.input(e=>e.type("text").name("interval").value(state.ReqDuration.ToString())),
H.br(),H.br(),
H.input(e=>e.type("submit").value("Set Interval").css("btn btn-primary"))
)
)
)
)
)
));
}
public HElement RSSTr(KeyValuePair<string,YoutubeRSS> item)
{ string url = $"https://www.youtube.com/channel/{item.Value.ID}";
return H.tr(
H.td(item.Key),
H.td(H.A(url, url)),
H.td(
H.form(
H.input(e => e.type("submit").css("btn-danger").value("X")),
H.input(e => e.type("hidden").name("operation").value("delete")),
H.input(e => e.type("hidden").name("key").value(item.Value.ID)
)
)));
}
public void Autofetch(HttpListenerRequest req, HttpListenerResponse resp, Dictionary<string, string> args)
{
resp.AsText(GetSite().ToString());
}
public void AutofetchChange(HttpListenerRequest req, HttpListenerResponse resp, Dictionary<string, string> args)
{
TimerEnabled = false;
req.ParseBody(args);
if (args.ContainsKey("operation"))
{
string operation = args["operation"];
switch (operation)
{
case "add":
if(args.ContainsKey("key") && args.ContainsKey("value"))
{
string key = args["key"];
string value = args["value"];
ChannelId? id = ChannelId.TryParse(value);
if(id.HasValue)
{
state.VideoRSS.Add(key, new YoutubeRSS(this, id.Value));
}
}
break;
case "delete":
if (args.ContainsKey("key"))
{
string key = args["key"];
if(state.VideoRSS.ContainsKey(key))
{
state.VideoRSS.Remove(key);
}
}
break;
case "clear":
state.VideoRSS.Clear();
break;
case "setinterval":
if (args.ContainsKey("interval"))
{
TimeSpan span;
if (TimeSpan.TryParse(args["interval"], out span))
{
state.ReqDuration = span;
}
}
break;
}
}
Save();
resp.AsText("<script>window.location.href=\".\"</script>");
}
public void Save()
{
string videoInfo = Path.Combine(StorageLocation, "autofetch.json");
File.WriteAllText(videoInfo, JsonConvert.SerializeObject(state));
SetTimer(state.ReqDuration);
}
public void Load()
{
string videoInfo = Path.Combine(StorageLocation, "autofetch.json");
if (File.Exists(videoInfo))
{
state = JsonConvert.DeserializeObject<RSSState>(File.ReadAllText(videoInfo));
foreach(var item in state.VideoRSS)
{
item.Value.SetApi(this);
}
}
else
{
state = new RSSState();
state.VideoRSS = new Dictionary<string, YoutubeRSS>();
}
}
}
public class YoutubeRSS
{
Api api;
public string ID { get; set; }
public YoutubeRSS(Api api,string id)
{
this.api = api;
this.ID = id;
// UCZkURf9tDolFOeuw_4RD7XQ
}
public YoutubeRSS()
{
}
public void SetApi(Api api)
{
this.api = api;
}
public void Scan()
{
var xml = System.Xml.XmlReader.Create("https://www.youtube.com/feeds/videos.xml?channel_id=" + this.ID);
var feed = System.ServiceModel.Syndication.SyndicationFeed.Load(xml);
foreach (var item in feed.Items)
{
string id = item.Id;
api.AddItem(id.Substring("yt:videoId".Length - 1));
}
}
}
}