269 lines
11 KiB
C#
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));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|