tesses-cms/Tesses.CMS/Class1.cs

2267 lines
97 KiB
C#
Raw Normal View History

2023-12-16 01:40:05 +00:00
using System;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Tesses.WebServer;
using Scriban;
using System.Collections.Generic;
using Tesses.WebServer.Swagme;
using System.Net;
using System.Web;
using System.Text;
using System.Xml;
using Microsoft.SyndicationFeed.Rss;
using Microsoft.SyndicationFeed.Atom;
using Microsoft.SyndicationFeed;
using PlaylistsNET.Models;
using PlaylistsNET.Content;
using System.Security.Cryptography;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
2024-01-04 02:53:13 +00:00
using MailKit.Net.Smtp;
using MimeKit;
2023-12-16 01:40:05 +00:00
namespace Tesses.CMS
{
2024-01-04 02:53:13 +00:00
public class Subtitle
{
[JsonProperty("end")]
public double End {get;set;}
[JsonProperty("begin")]
public double Begin {get;set;}
[JsonProperty("text")]
public string Text {get;set;}
public void ToWebVTT(TextWriter writer)
{
var begin=TimeSpan.FromSeconds(Begin);
var end = TimeSpan.FromSeconds(End);
try{
writer.WriteLine($"{begin.ToString("hh\\:mm\\:ss\\.fff")} --> {end.ToString("hh\\:mm\\:ss\\.fff")}");
}catch(Exception ex)
{
Console.WriteLine(ex);
}
writer.WriteLine(HttpUtility.HtmlEncode(Text));
}
public void ToSrt(TextWriter writer)
{
var begin=TimeSpan.FromSeconds(Begin);
var end = TimeSpan.FromSeconds(End);
writer.WriteLine($"{begin.ToString("hh\\:mm\\:ss\\,fff")} --> {end.ToString("hh\\:mm\\:ss\\,fff")}");
writer.WriteLine(Text);
}
public static void ToWebVTT(TextWriter writer,List<Subtitle> subtitles)
{
writer.WriteLine("WEBVTT");
for(int i = 0;i<subtitles.Count;i++)
{
writer.WriteLine();
writer.WriteLine(i+1);
subtitles[i].ToWebVTT(writer);
}
}
public static void ToSrt(TextWriter writer,List<Subtitle> subtitles)
{
for(int i = 0;i<subtitles.Count;i++)
{
if(i>0) writer.WriteLine();
writer.WriteLine(i+1);
subtitles[i].ToSrt(writer);
}
}
}
2023-12-16 01:40:05 +00:00
public class CMSServer
{
2024-01-04 02:53:13 +00:00
public static readonly Language[] Languages=new Language[]{
new Language()
{
LangCode="en-US",
LangCodeVideo="en",
LangTitle="English (US)"
},
new Language()
{
LangCode="en-UK",
LangCodeVideo="en",
LangTitle="English (UK)"
},
new Language()
{
LangCode="ar-AE",
LangCodeVideo="ar",
LangTitle="Arabic (AE)"
},
new Language()
{
LangCode="zh-CN",
LangCodeVideo="zh",
LangTitle="Chinese (CN)"
},
new Language()
{
LangCode = "zh-TW",
LangCodeVideo="zh",
LangTitle="Chinese (TW)"
},
new Language()
{
LangCode="cs-CZ",
LangCodeVideo ="cs",
LangTitle = "Czech (CZ)"
},
new Language()
{
LangCode="da-DK",
LangCodeVideo="da",
LangTitle="Danish (DK)"
},
new Language()
{
LangCode="in-ID",
LangCodeVideo="in",
LangTitle = "Indonesian (ID)"
},
new Language()
{
LangCode="ms-MY",
LangCodeVideo="ms",
LangTitle="Malaysian (MY)"
},
new Language()
{
LangCode="nl-NL",
LangCodeVideo="nl",
LangTitle="Dutch (NL)"
},
new Language()
{
LangCode = "fr-FR",
LangCodeVideo="fr",
LangTitle="French (FR)"
},
new Language()
{
LangCode="fi-FI",
LangCodeVideo="fi",
LangTitle="Finnish (FI)"
},
new Language()
{
LangCode="de-DE",
LangCodeVideo="de",
LangTitle="German (DE)"
},
new Language()
{
LangCode = "it-IT",
LangCodeVideo="it",
LangTitle="Italian (IT)"
},
new Language()
{
LangCode="ja-JP",
LangCodeVideo="ja",
LangTitle="Japanese (JP)"
},
new Language()
{
LangCode="ko-KR",
LangCodeVideo = "ko",
LangTitle="Korean (KR)"
},
new Language()
{
LangCode = "no-NO",
LangCodeVideo = "no",
LangTitle="Norwegian (NO)"
},
new Language()
{
LangCode = "pl-PL",
LangCodeVideo = "pl",
LangTitle = "Polish (PL)"
},
new Language()
{
LangCode = "pt-BR",
LangCodeVideo = "pt",
LangTitle = "Portuguese (BR)"
},
new Language()
{
LangCode="ro-RO",
LangCodeVideo = "ro",
LangTitle = "Romanian (RO)"
},
new Language()
{
LangCode = "ru-RU",
LangCodeVideo = "ru",
LangTitle = "Russian (RU)"
},
new Language()
{
LangCode = "es-ES",
LangCodeVideo = "es",
LangTitle = "Spanish (ES)"
},
new Language()
{
LangCode = "sv-SE",
LangCodeVideo = "sv",
LangTitle = "Swedish (SE)"
},
new Language()
{
LangCode = "th-TH",
LangCodeVideo = "th",
LangTitle = "Thai (TH)"
},
new Language()
{
LangCode = "tl-PH",
LangCodeVideo = "tl",
LangTitle = "Filipino (PH)"
},
new Language()
{
LangCode = "tr-TR",
LangCodeVideo = "tr",
LangTitle = "Turkish (TR)"
}
};
public class Language
{
public string LangCode {get;set;}
public string LangCodeVideo {get;set;}
public string LangTitle {get;set;}
}
public MovieContentMetaData GetMovieContentMetaData(CMSConfiguration configuration, string user, string movie)
{
var _movie= new MovieContentMetaData(){
PosterUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/poster.jpg",
ThumbnailUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/thumbnail.jpg",
BrowserStream = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/browser.mp4",
DownloadStream = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.mp4",
MovieTorrentUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.torrent",
MovieWithExtrasTorrentUrl=$"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}_withextras.torrent"
};
string subDir=Path.Combine(path,user, "movie",movie,"subtitles");
if(Directory.Exists(subDir))
foreach(var language in Directory.GetDirectories(subDir))
{
string languageCode=Path.GetFileName(language); //en-US for english
_movie.SubtitlesStreams.Add(new SubtitleStream(){LanguageCode=languageCode, VttUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/subtitles/{languageCode}/{movie}.vtt", SrtUrl=$"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/subtitles/{languageCode}/{movie}.srt"});
}
string extrasPath = Path.Combine(path, user, "movie",movie,"extras");
if(Directory.Exists(extrasPath))
FindExtras( $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/extras/",extrasPath,_movie.ExtraStreams);
return _movie;
}
private void FindExtras(string extras,string v, List<ExtraDataStream> extraStreams)
{
foreach(var d in Directory.GetDirectories(v))
{
var dirname = Path.GetFileName(d);
string url = $"{extras}{dirname}/";
ExtraDataStream dataStream=new ExtraDataStream();
dataStream.IsDir=true;
dataStream.Name = dirname;
dataStream.Url = url;
FindExtras(url,d,dataStream.Items);
extraStreams.Add(dataStream);
}
foreach(var f in Directory.GetFiles(v))
{
var filename = Path.GetFileName(f);
var url = $"{extras}{filename}";
ExtraDataStream dataStream=new ExtraDataStream();
dataStream.IsDir=false;
dataStream.Name = filename;
dataStream.Url = url;
extraStreams.Add(dataStream);
}
}
EmailCreator Creator {get;set;}
2023-12-16 01:40:05 +00:00
Template pageShell;
Template pageIndex;
Template pageDevcenter;
2024-01-04 02:53:13 +00:00
Template pageShow;
Template pageSeason;
Template pageShows;
2023-12-16 01:40:05 +00:00
Template pageMovie;
Template pageMovies;
Template pageWatchMovie;
Template pageUpload;
Template pageUsers;
Template pageEditMovieDetails;
2024-01-04 02:53:13 +00:00
Template pageEditShowDetails;
Template pageEditSeasonDetails;
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
Template pageExtrasViewer;
Template pageEditUser;
Template aboutUser;
Template pageSubtitleLangList;
Template pageSubtitleEditor;
private Template pageVerifyEmail;
private Template pageManage;
2023-12-16 01:40:05 +00:00
RouteServer routeServer;
PathValueServer usersPathValueServer;
PathValueServer moviePathValueServer;
2024-01-04 02:53:13 +00:00
PathValueServer showPathValueServer;
PathValueServer seasonPathValueServer;
MountableServer showMountableServer;
2023-12-16 01:40:05 +00:00
MountableServer usersMountableServer;
2024-01-04 02:53:13 +00:00
MountableServer seasonMountableServer;
2023-12-16 01:40:05 +00:00
RouteServer movieRouteServer;
2024-01-04 02:53:13 +00:00
RouteServer showRouteServer;
RouteServer seasonRouteServer;
2023-12-16 01:40:05 +00:00
IContentProvider provider;
string path;
public CMSServer(string configpath,IContentProvider provider)
{
this.provider=provider;
usersMountableServer=new MountableServer();
usersPathValueServer = new PathValueServer(usersMountableServer);
movieRouteServer=new RouteServer();
2024-01-04 02:53:13 +00:00
showRouteServer = new RouteServer();
seasonRouteServer=new RouteServer();
2023-12-16 01:40:05 +00:00
moviePathValueServer = new PathValueServer(movieRouteServer);
2024-01-04 02:53:13 +00:00
showMountableServer=new MountableServer(showRouteServer);
showPathValueServer=new PathValueServer(showMountableServer);
seasonMountableServer = new MountableServer(seasonRouteServer);
seasonPathValueServer=new PathValueServer(seasonMountableServer);
2023-12-16 01:40:05 +00:00
string configJson = Path.Combine(configpath,"config.json");
if(File.Exists(configJson))
{
Configuration = JsonConvert.DeserializeObject<CMSConfiguration>(File.ReadAllText(configJson));
}
if(Configuration.BittorrentTrackers.Count == 0)
{
Configuration.BittorrentTrackers.AddRange( new string[]{
"https://t1.hloli.org:443/announce",
"http://1337.abcvg.info:80/announce",
"http://tracker.renfei.net:8080/announce",
"http://tracker.gbitt.info:80/announce",
"http://p2p.0g.cx:6969/announce",
"https://tracker.foreverpirates.co:443/announce"
});
}
2024-01-04 02:53:13 +00:00
Creator =new EmailCreator(Configuration);
2023-12-16 01:40:05 +00:00
pageShell=Template.Parse(AssetProvider.ReadAllText("/PageShell.html"));
pageIndex = Template.Parse(AssetProvider.ReadAllText("/Index.html"));
pageDevcenter = Template.Parse(AssetProvider.ReadAllText("/Devcenter.html"));
pageMovie = Template.Parse(AssetProvider.ReadAllText("/MoviePage.html"));
pageMovies = Template.Parse(AssetProvider.ReadAllText("/MoviesPage.html"));
pageUsers = Template.Parse(AssetProvider.ReadAllText("/UsersPage.html"));
pageWatchMovie = Template.Parse(AssetProvider.ReadAllText("/WatchMovie.html"));
pageUpload = Template.Parse(AssetProvider.ReadAllText("/Upload.html"));
pageEditMovieDetails = Template.Parse(AssetProvider.ReadAllText("/EditMovieDetails.html"));
2024-01-04 02:53:13 +00:00
pageEditShowDetails = Template.Parse(AssetProvider.ReadAllText("/EditShowDetails.html"));
pageEditSeasonDetails = Template.Parse(AssetProvider.ReadAllText("/EditSeasonDetails.html"));
pageExtrasViewer = Template.Parse(AssetProvider.ReadAllText("/ExtrasViewer.html"));
pageEditUser = Template.Parse(AssetProvider.ReadAllText("/AccountEditor.html"));
aboutUser = Template.Parse(AssetProvider.ReadAllText("/AboutUser.html"));
pageVerifyEmail = Template.Parse(AssetProvider.ReadAllText("/VerifyEmailWeb.html"));
pageManage = Template.Parse(AssetProvider.ReadAllText("/ManageHtml.html"));
pageSubtitleLangList = Template.Parse(AssetProvider.ReadAllText("/SubtitleLangList.html"));
pageSubtitleEditor = Template.Parse(AssetProvider.ReadAllText("/SubtitleEditor.html"));
pageShow = Template.Parse(AssetProvider.ReadAllText("/ShowPage.html"));
pageSeason = Template.Parse(AssetProvider.ReadAllText("/SeasonPage.html"));
pageShows = Template.Parse(AssetProvider.ReadAllText("/ShowsPage.html"));
2023-12-16 01:40:05 +00:00
MountableServer mountableServer = new MountableServer(new AssetProvider());
path=Path.Combine(configpath,"content");
mountableServer.Mount("/content/",new StaticServer(path){AllowListingDirectories=true});
mountableServer.Mount("/api/v1/",CreateSwagme());
mountableServer.Mount("/user/",usersPathValueServer);
routeServer = new RouteServer(mountableServer);
routeServer.Add("/",Index,"GET");
routeServer.Add("/devcenter",Devcenter,"GET");
routeServer.Add("/upload",UploadPage1,"GET");
routeServer.Add("/upload",Upload,"POST");
routeServer.Add("/users",UsersAsync,"GET");
routeServer.Add("/login",LoginAsync);
2024-01-04 02:53:13 +00:00
routeServer.Add("/logout",LogoutAsync);
2023-12-16 01:40:05 +00:00
routeServer.Add("/login",LoginPostAsync,"POST");
routeServer.Add("/signup",SignupAsync);
routeServer.Add("/signup",SignupPostAsync,"POST");
2024-01-04 02:53:13 +00:00
routeServer.Add("/account",AccountAsync);
routeServer.Add("/account",AccountPostAsync,"POST");
routeServer.Add("/verify",VerifyAsync);
routeServer.Add("/resend",ResendVerification,"GET");
routeServer.Add("/manage",ManageAsync);
routeServer.Add("/manage",ManagePostAsync,"POST");
routeServer.Add("/impersonate", ImpersonateAsync);
2023-12-16 01:40:05 +00:00
RegisterUsersPath();
Task.Factory.StartNew(async()=>{
while(Running)
{
if(tasks.TryDequeue(out var item))
{
await item();
}
await Task.Delay(TimeSpan.FromSeconds(0.125));
}
}).Wait(0);
2024-01-04 02:53:13 +00:00
}
public async Task ManagePostAsync(ServerContext ctx)
{
ctx.ParseBody();
var account=GetAccount(ctx);
if(account != null && account.IsAdmin)
{
if(ctx.QueryParams.TryGetFirst("name",out var name))
{
bool isinvited = ctx.QueryParams.ContainsKey("invited");
bool isverified = ctx.QueryParams.ContainsKey("verified");
bool isadmin = ctx.QueryParams.ContainsKey("admin");
var a = provider.GetUserAccount(name);
a.IsInvited = isinvited;
a.IsVerified = isverified;
a.IsAdmin = isadmin;
provider.UpdateUser(a);
}
}
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/manage");
}
public async Task ImpersonateAsync(ServerContext ctx)
{
var account=GetAccount(ctx,out var cookie);
if(account != null && account.IsAdmin)
{
if(ctx.QueryParams.TryGetFirst("user",out var user))
{
var _user = provider.GetUserAccount(user);
if(_user != null)
{
provider.ChangeSession(cookie,_user.Id);
}
}
}
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
//impersonate
}
private async Task ManageAsync(ServerContext ctx)
{
var account=GetAccount(ctx); List<object> users = new List<object>();
if(account != null && account.IsAdmin)
{
int i =0;
foreach(var user in provider.GetUsers())
{
if(account.Id != user.Id)
{
string impersonate = $"{Configuration.Root.TrimEnd('/')}/impersonate?user={user.Username}";
users.Add(new {nameattr=HttpUtility.HtmlAttributeEncode(user.Username), propername=HttpUtility.HtmlEncode(user.ProperName), name = HttpUtility.HtmlEncode(user.Username),isverified=user.IsVerified, isadmin = user.IsAdmin, isinvited=user.IsInvited,impersonate=impersonate,i=i});
i++;
}
}
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageManage.RenderAsync(new{users=users})));
}
private async Task ResendVerification(ServerContext ctx)
{
var account=GetAccount(ctx);
if(account != null){
if(account.IsVerified)
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
else
{
var code=GetOrGenerateVerification(account);
await Creator.SendVerificationEmailAsync(account,code);
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageVerifyEmail.RenderAsync(new{Email=account.Email})));
}
}
}
private async Task VerifyAsync(ServerContext ctx)
{
bool failed=true;
if(ctx.QueryParams.TryGetFirst("token",out var token))
{
var item = provider.GetVerificationAccount(token);
if(item.HasValue)
{
var a=provider.GetUserById(item.Value);
a.IsVerified = true;
provider.UpdateUser(a);
failed=false;
provider.DeleteVerificationCode(token);
}
if(!failed)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Your account has been verified, click login to login.</h1><a href=\"./login\" class=\"btn btn-primary\">Login</a>"));
}
}
if(failed)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Failed to verify account</h1>"));
}
}
private async Task AccountPostAsync(ServerContext ctx)
{
ctx.ParseBody();
var account = GetAccount(ctx);
if(account != null)
{
if(ctx.QueryParams.TryGetFirst("about_me",out var about_me) && ctx.QueryParams.TryGetFirst("proper_name",out var proper_name))
{
account.AboutMe = about_me;
account.ProperName = proper_name;
provider.UpdateUser(account);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
}
}
else
{
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
}
}
private async Task AccountAsync(ServerContext ctx)
{
var account = GetAccount(ctx);
if(account != null)
{
//account page
var res=new{admin = account.IsAdmin,Notverified = !account.IsVerified, Propername = HttpUtility.HtmlAttributeEncode(account.ProperName), Aboutme=HttpUtility.HtmlEncode(account.AboutMe)};
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageEditUser.RenderAsync(res)));
}
else
{
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
}
}
private async Task LogoutAsync(ServerContext ctx)
{
Logout(ctx);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
}
2023-12-16 01:40:05 +00:00
public bool Running =true;
public async Task LoginAsync(ServerContext ctx)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,await AssetProvider.ReadAllTextAsync("/LoginPage.html")));
}
public async Task SignupAsync(ServerContext ctx)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,await AssetProvider.ReadAllTextAsync("/SignupPage.html")));
}
public async Task SignupPostAsync(ServerContext ctx)
{
ctx.ParseBody();
if(ctx.QueryParams.TryGetFirst("email", out var email) && ctx.QueryParams.TryGetFirst("name", out var name) && ctx.QueryParams.TryGetFirst("proper_name",out var proper_name) && ctx.QueryParams.TryGetFirst("password",out var password) && ctx.QueryParams.TryGetFirst("confirm_password",out var confirm_password))
{
bool error = false,emailTaken=false, nameTaken=false, properNameTaken=false, passwordDontMatch=false, passwordNotGoodEnough=false;
foreach(var user in provider.GetUsers())
{
if(user.Username == name)
{
nameTaken=true;
error=true;
}
if(user.Email == email)
{
emailTaken=true;
error=true;
}
if(user.ProperName == proper_name)
{
properNameTaken=true;
error=true;
}
}
if(password != confirm_password)
{
error=true;
passwordDontMatch=true;
}
if(password.Length < 10)
{
error=true;
passwordNotGoodEnough=true;
}
if(error)
{
StringBuilder b=new StringBuilder();
if(emailTaken)
{
b.AppendLine("<h1>Email is taken</h1>");
}
if(nameTaken)
{
b.AppendLine("<h1>Name is taken</h1>");
}
if(properNameTaken)
{
b.AppendLine("<h1>Proper Name is taken");
}
if(passwordNotGoodEnough)
{
b.AppendLine("<h1>Password not good enough</h1>");
}
if(passwordDontMatch)
{
b.AppendLine("<h1>Passwords don't match</h1>");
}
await ctx.SendTextAsync(b.ToString());
}
else
{
provider.CreateUser(Configuration,name,proper_name,email,password);
2024-01-04 02:53:13 +00:00
var account=provider.GetUserAccount(name);
if(account.IsVerified)
2023-12-16 01:40:05 +00:00
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
2024-01-04 02:53:13 +00:00
else
{
var code=GetOrGenerateVerification(account);
await Creator.SendVerificationEmailAsync(account,code);
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageVerifyEmail.RenderAsync(new{Email=email})));
}
2023-12-16 01:40:05 +00:00
}
}
}
2024-01-04 02:53:13 +00:00
private string GetOrGenerateVerification(UserAccount account)
{
var now=DateTime.Now;
byte[] bytes=new byte[32];
string token;
using(var rng = RandomNumberGenerator.Create())
do {
rng.GetBytes(bytes);
token=Convert.ToBase64String(bytes);
} while(provider.ContainsVerificationCode(token));
provider.CreateVerificationCode(token,account.Id);
return token;
}
2023-12-16 01:40:05 +00:00
public async Task LoginPostAsync(ServerContext ctx)
{
ctx.ParseBody();
if(ctx.QueryParams.TryGetFirst("email",out var email) && ctx.QueryParams.TryGetFirst("password",out var password))
{
foreach(var a in provider.GetUsers())
{
if(a.Email != email) continue;
if(a.Email == email && a.PasswordCorrect(password))
{
//we got it
byte[] bytes=new byte[32];
string cookie;
using(var rng = RandomNumberGenerator.Create())
do {
rng.GetBytes(bytes);
cookie=Convert.ToBase64String(bytes);
2024-01-04 02:53:13 +00:00
} while(provider.ContainsSession(cookie));
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
provider.CreateSession(cookie,a.Id);
2023-12-16 01:40:05 +00:00
ctx.ResponseHeaders.Add("Set-Cookie",$"Session={cookie}; Path=/");
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
return;
}
}
ctx.StatusCode=401;
await ctx.SendTextAsync("<h1>Incorrect</h1><a href=\"./\">Home</a> | <a href=\"./login\">Login</a>");
}
}
2024-01-04 02:53:13 +00:00
private void Logout(ServerContext ctx)
{
if(ctx.RequestHeaders.TryGetValue("Cookie",out var cookies))
{
foreach(var c in cookies)
{
var co = c.Split(new char[]{'='},2);
if(co.Length == 2 && co[0] == "Session")
{
if(provider.ContainsSession(co[1]))
{
provider.DeleteSession(co[1]);
return;
}
}
}
}
}
2023-12-16 01:40:05 +00:00
private UserAccount GetAccount(ServerContext ctx)
{
2024-01-04 02:53:13 +00:00
return GetAccount(ctx,out var s);
}
private UserAccount GetAccount(ServerContext ctx,out string cookie)
{
cookie="";
2023-12-16 01:40:05 +00:00
if(ctx.RequestHeaders.TryGetValue("Cookie",out var cookies))
{
foreach(var c in cookies)
{
2024-01-04 02:53:13 +00:00
2023-12-16 01:40:05 +00:00
var co = c.Split(new char[]{'='},2);
if(co.Length == 2 && co[0] == "Session")
{
2024-01-04 02:53:13 +00:00
cookie = co[1];
long? account = provider.GetSession(cookie);
if(account.HasValue)
return provider.GetUserById(account.Value);
2023-12-16 01:40:05 +00:00
}
}
}
return null;
}
private async Task UsersAsync(ServerContext ctx)
{
List<object> users=new List<object>();
foreach(var user in provider.GetUsers())
{
2024-01-04 02:53:13 +00:00
if(!user.IsAdmin && Configuration.Publish == CMSPublish.Admin)
{
//await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You can't upload content</h1>"));
continue;
}
if(!(user.IsAdmin || user.IsInvited) && Configuration.Publish == CMSPublish.RequireInvite)
{
//await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You can't upload content</h1>"));
continue;
}
if(!(user.IsAdmin || user.IsInvited || user.IsVerified))
{
//await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You can't upload content</h1>"));
continue;
}
2023-12-16 01:40:05 +00:00
users.Add(user.Scriban());
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageUsers.RenderAsync(new {
Users=users,
rooturl=$"{Configuration.Root.TrimEnd('/')}/"
})));
}
const string badC="/\\\"&,?|:;*@!# ";
private string FixString(string str)
{
StringBuilder b=new StringBuilder();
foreach(var item in str)
{
if(char.IsControl(item))
{
continue;
}
2024-01-04 02:53:13 +00:00
2023-12-16 01:40:05 +00:00
if(item >= 127) continue;
if(badC.Contains(item.ToString())) continue;
b.Append(item.ToString());
}
return b.ToString();
}
private async Task Upload(ServerContext ctx)
{
ctx.ParseBody();
if(ctx.QueryParams.TryGetFirst("name",out var name) && ctx.QueryParams.TryGetFirst("proper_name",out var proper_name) && ctx.QueryParams.TryGetFirst("type", out var type) && ctx.QueryParams.TryGetFirst("description",out var description))
{
var account=GetAccount(ctx);
if(account != null)
{
if(!account.IsAdmin && Configuration.Publish == CMSPublish.Admin)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You can't upload content</h1>"));
return;
}
if(!(account.IsAdmin || account.IsInvited) && Configuration.Publish == CMSPublish.RequireInvite)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You can't upload content</h1>"));
return;
}
if(!(account.IsAdmin || account.IsInvited || account.IsVerified))
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You can't upload content</h1>"));
return;
}
name = FixString(name);
switch(type)
{
case "movie":
2024-01-04 02:53:13 +00:00
provider.CreateMovie(account.Username,name,proper_name,description.Replace("\r",""));
2023-12-16 01:40:05 +00:00
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{account.Username}/movie/{name}/edit");
break;
2024-01-04 02:53:13 +00:00
case "show":
provider.CreateShow(account.Username,name,proper_name,description);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{account.Username}/show/{name}/edit");
break;
}
}
else
{
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
}
}
else
{
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
}
}
public async Task UploadPage1(ServerContext ctx)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,await RenderUpload1Async(),Configuration.RelativeNavUrl("Devcenter","devcenter")));
}
public IServer Movies()
{
RouteServer routeServer=new RouteServer();
routeServer.Add("/",MoviesAsync);
return routeServer;
}
public async Task MoviesAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
List<object> movies=new List<object>();
foreach(var item in provider.GetMovies(user))
{
var data = GetMovieContentMetaData(Configuration,user,item.Name);
movies.Add(item.Scriban(data.ThumbnailUrl));
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageMovies.RenderAsync(new{Movies=movies})));
}
private void RegisterUsersPath()
{
RouteServer routeServer=new RouteServer();
routeServer.Add("/",UserPageAsync);
routeServer.Add("/about",AboutAsync);
usersMountableServer.Mount("/",routeServer);
usersMountableServer.Mount("/movies/",Movies());
usersMountableServer.Mount("/movie/",moviePathValueServer);
usersMountableServer.Mount("/shows/",Shows());
usersMountableServer.Mount("/show/",showPathValueServer);
RegisterMoviePath();
RegisterShowPath();
}
private void RegisterShowPath()
{
showRouteServer.Add("/",ShowPageAsync);
showRouteServer.Add("/edit",EditShowPageAsync);
showRouteServer.Add("/edit",EditShowPagePostAsync,"POST");
showRouteServer.Add("/upload",UploadShowStreamAsync,"POST");
showRouteServer.Add("/addseason",AddSeasonAsync,"POST");
showMountableServer.Mount("/season/",seasonPathValueServer);
RegisterSeasonPath();
}
private void RegisterSeasonPath()
{
seasonRouteServer.Add("/",SeasonPageAsync);
seasonRouteServer.Add("/edit",EditSeasonPageAsync);
seasonRouteServer.Add("/edit",EditSeasonPagePostAsync,"POST");
seasonRouteServer.Add("/addepisode",AddEpisodeAsync,"POST");
seasonRouteServer.Add("/upload",UploadSeasonStreamAsync,"POST");
}
private async Task AddEpisodeAsync(ServerContext ctx)
{
ctx.ParseBody();
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
string season = seasonPathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _show = provider.GetShow(user,show);
int seasonNo = 1;
if(!int.TryParse(season,out seasonNo))
seasonNo = 1;
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
int episode=1;
if(ctx.QueryParams.TryGetFirst("number",out var number) && !int.TryParse(number,out episode))
episode=1;
if(ctx.QueryParams.TryGetFirst("proper_name",out var proper_name) && ctx.QueryParams.TryGetFirst("description",out var description) && ctx.QueryParams.TryGetFirst("name",out var name))
{
provider.CreateEpisode(user,show,seasonNo,episode,name,proper_name,description);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{show}/season/{seasonNo}/episode/{episode}/edit");
}
else
{
}
}
}
private Task EditSeasonPagePostAsync(ServerContext ctx)
{
throw new NotImplementedException();
}
private async Task EditSeasonPageAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
string seasonS = seasonPathValueServer.GetValue(ctx);
int season = 1;
if(!int.TryParse(seasonS,out season))
season=1;
var me = GetAccount(ctx);
var _season = provider.GetSeason(user,show,season);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
if(_season != null)
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageEditSeasonDetails.RenderAsync(new{Propername=System.Web.HttpUtility.HtmlAttributeEncode( _season.ProperName),newseasonnumber = provider.EpisodeCount(user,show,season)+1,Description=System.Web.HttpUtility.HtmlEncode(_season.Description)})));
}
else
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You are unauthorized to edit this</h1>"));
}
}
private async Task SeasonPageAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
string season = seasonPathValueServer.GetValue(ctx);
var _show= provider.GetShow(user,show);
var _user = provider.GetUserAccount(user);
if(!int.TryParse(season,out var seasonNo))
seasonNo=1;
var _season = provider.GetSeason(user,show,seasonNo);
var me = GetAccount(ctx);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
object value;
if(_show != null && _user != null && _season != null)
{
string showDir = Path.Combine(this.path,user,"show",show);
string thumb = File.Exists(Path.Combine(showDir,user,"show",show,$"Season {_season.SeasonNumber.ToString("D2")}","thumbnail.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{_season.SeasonNumber.ToString("D2")}/thumbnail.jpg" : $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg";
List<object> episodes=new List<object>();
for(int i = 1;i<=provider.EpisodeCount(user,show,seasonNo);i++)
{
var item = provider.GetEpisode(user,show,seasonNo,i);
if(item != null)
//var data = GetMovieContentMetaData(Configuration,user,item.Name);
episodes.Add(item.Scriban(Configuration,path,user,show));
}
value = new{ episodes, editable=me!=null, userproper=HttpUtility.HtmlEncode(_user.ProperName), username=HttpUtility.HtmlEncode(user), rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasshow=true,seasonthumbnail=thumb,seasonproper=HttpUtility.HtmlEncode(_season.ProperName),showproper=HttpUtility.HtmlEncode(_show.ProperName),showname=HttpUtility.HtmlEncode(_show.Name),seasondescription=DescriptLinkUtils(_season.Description ?? "").Replace("\n","<br>")};
}
else
{
value = new{ username=user, rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasshow=false};
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageSeason.RenderAsync(value)));
}
private async Task AddSeasonAsync(ServerContext ctx)
{
ctx.ParseBody();
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _show = provider.GetShow(user,show);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
int season = 1;
if(ctx.QueryParams.TryGetFirst("number",out var number) && !int.TryParse(number,out season))
season=1;
if(ctx.QueryParams.TryGetFirst("proper_name",out var proper_name) && ctx.QueryParams.TryGetFirst("description",out var description))
{
provider.CreateSeason(user,show,season,proper_name,description);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{show}/season/{season}/edit");
}
else
{
}
}
}
private Task EditShowPagePostAsync(ServerContext ctx)
{
throw new NotImplementedException();
}
private async Task EditShowPageAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _show = provider.GetShow(user,show);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
if(_show != null)
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageEditShowDetails.RenderAsync(new{Propername=System.Web.HttpUtility.HtmlAttributeEncode( _show.ProperName),newseasonnumber = provider.SeasonCount(user,show)+1,Description=System.Web.HttpUtility.HtmlEncode(_show.Description)})));
}
else
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You are unauthorized to edit this</h1>"));
}
}
private async Task ShowPageAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
var _show= provider.GetShow(user,show);
var _user = provider.GetUserAccount(user);
var me = GetAccount(ctx);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
object value;
if(_show != null && _user != null)
{
string showDir = Path.Combine(this.path,user,"show",show);
bool torrent= File.Exists(Path.Combine(showDir,$"{show}.torrent"));
bool torrent_wextra= File.Exists(Path.Combine(showDir,$"{show}_withextras.torrent"));
bool extrasexists = Directory.Exists(Path.Combine(showDir,"extras")) || me != null;
string thumb = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg";
List<object> seasons=new List<object>();
for(int i = 1;i<=provider.SeasonCount(user,show);i++)
{
var item = provider.GetSeason(user,show,i);
if(item != null)
//var data = GetMovieContentMetaData(Configuration,user,item.Name);
seasons.Add(item.Scriban(Configuration,path,user,show));
}
value = new{ seasons,extrasexists=extrasexists, torrentexists=torrent, torrentwextraexists=torrent_wextra, torrent=$"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/{show}.torrent",torrentwextra=$"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/{show}_withextras.torrent" , editable=me!=null, userproper=HttpUtility.HtmlEncode(_user.ProperName), username=HttpUtility.HtmlEncode(user), rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasshow=true,showthumbnail=thumb,showproper=HttpUtility.HtmlEncode(_show.ProperName),showname=HttpUtility.HtmlEncode(_show.Name),showdescription=DescriptLinkUtils(_show.Description ?? "").Replace("\n","<br>")};
}
else
{
value = new{ username=user, rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasshow=false};
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageShow.RenderAsync(value)));
}
private IServer Shows()
{
RouteServer routeServer=new RouteServer();
routeServer.Add("/",ShowsAsync);
return routeServer;
}
private async Task ShowsAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
List<object> shows=new List<object>();
foreach(var item in provider.GetShows(user))
{
//var data = GetMovieContentMetaData(Configuration,user,item.Name);
shows.Add(item.Scriban(Configuration,user));
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageShows.RenderAsync(new{Shows=shows})));
}
private bool StartsWithAt(string str,int indexof,string startsWith)
{
if(str.Length-indexof < startsWith.Length) return false;
for(int i = 0;i<startsWith.Length;i++)
{
if(str[i+indexof] != startsWith[i]) return false;
}
return true;
}
private string DescriptLinkUtils(string url)
{
StringBuilder b=new StringBuilder();
for(int i = 0;i<url.Length;i++)
{
if(StartsWithAt(url,i,"http:") || StartsWithAt(url,i,"https:") || StartsWithAt(url,i,"ftp:") || StartsWithAt(url,i,"ftps:") || StartsWithAt(url,i,"sftp:") || StartsWithAt(url,i,"magnet:"))
{
StringBuilder b2=new StringBuilder();
for(;i<url.Length;i++)
{
if(url[i] == '\n' || url[i] == ' ')
{
break;
}
b2.Append(url[i]);
2023-12-16 01:40:05 +00:00
}
2024-01-04 02:53:13 +00:00
b.Append($"<a href=\"{HttpUtility.HtmlAttributeEncode(b2.ToString())}\">{HttpUtility.HtmlEncode(b2.ToString())}</a>");
}
else
{
b.Append(HttpUtility.HtmlEncode(url[i]));
}
}
return b.ToString();
}
private async Task AboutAsync(ServerContext ctx)
{
var username = usersPathValueServer.GetValue(ctx);
var accountData = provider.GetUserAccount(username);
if(accountData != null)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,await aboutUser.RenderAsync(new{Propername=HttpUtility.HtmlEncode(accountData.ProperName), Aboutme=DescriptLinkUtils(accountData.AboutMe ?? "").Replace("\n","<br>")})));
}
}
private async Task UserPageAsync(ServerContext ctx)
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,await AssetProvider.ReadAllTextAsync("/UserPage.html")));
}
ConcurrentQueue<Func<Task>> tasks=new ConcurrentQueue<Func<Task>>();
private void RegisterMoviePath()
{
movieRouteServer.Add("/",MoviePageAsync);
movieRouteServer.Add("/play",PlayMoviePageAsync);
movieRouteServer.Add("/edit",EditMoviePageAsync);
movieRouteServer.Add("/edit",EditMoviePagePostAsync,"POST");
movieRouteServer.Add("/upload",UploadMovieStreamAsync,"POST");
movieRouteServer.Add("/upload_extra",UploadMovieExtraAsync,"POST");
movieRouteServer.Add("/extras",ExtrasMoviePageAsync);
movieRouteServer.Add("/mkdir",ExtrasMovieMkdirAsync,"POST");
movieRouteServer.Add("/subtitles",SubtitlesMovieAsync);
movieRouteServer.Add("/subtitles",SubtitlesMoviePostAsync,"POST");
//http://192.168.0.158:62444/user/tesses/movie/MyGreatMovie/mkdir
}
private async Task SubtitlesMoviePostAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
if(ctx.QueryParams.TryGetFirst("lang",out var lang) && !string.IsNullOrWhiteSpace(lang))
{
var json = await ctx.ReadJsonAsync<List<Subtitle>>();
string langDir = Path.Combine(path,user,"movie",movie,"subtitles",lang);
Directory.CreateDirectory(langDir);
string langFile = Path.Combine(langDir,$"{movie}.json");
string vtt = Path.Combine(langDir,$"{movie}.vtt");
string srt = Path.Combine(langDir,$"{movie}.srt");
File.WriteAllText(langFile,JsonConvert.SerializeObject(json));
using(var vttFile=File.CreateText(vtt))
{
Subtitle.ToWebVTT(vttFile,json);
}
using(var srtFile=File.CreateText(srt))
{
Subtitle.ToSrt(srtFile,json);
}
await ctx.SendTextAsync("Success");
return;
}
}
ctx.StatusCode=400;
await ctx.SendTextAsync("Fail");
}
private async Task SubtitlesMovieAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{ if(ctx.QueryParams.TryGetFirst("lang",out var lang) && !string.IsNullOrWhiteSpace(lang))
{
string langDir = Path.Combine(path,user,"movie",movie,"subtitles",lang);
string langFile = Path.Combine(langDir,$"{movie}.json");
string browserfile = $"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/browser.mp4";
string json="";
bool hasjson=false;
if(File.Exists(langFile))
{
hasjson=true;
json=File.ReadAllText(langFile);
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageSubtitleEditor.RenderAsync(new{hasjson,json,lang,browserfile})));
}
else
{
List<object> languages = new List<object>();
foreach(var item in Languages)
{
languages.Add(new {code=item.LangCode, name=item.LangTitle});
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageSubtitleLangList.RenderAsync(new {languages})));
}
}
}
private async Task UploadMovieExtraAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
Directory.CreateDirectory(Path.Combine(path,user,"movie",movie));
var tmpFile = Path.Combine(path,user,"movie",movie,$"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
string filename="";
foreach(var item in ctx.ParseBody((n,fn,ct)=>{ filename=fn; return File.Create(tmpFile);}))
item.Value.Dispose();
if(ctx.QueryParams.TryGetFirst("parent",out var parent))
{
var _path = SanitizePath($"{parent.TrimStart('/').TrimEnd('/')}/{filename}").TrimStart('/');
var _path2=Path.Combine(path,user,"movie",movie,"extras",_path);
if(File.Exists(_path2))
File.Delete(_path2);
File.Move(tmpFile,_path2);
ScheduleTask(async()=>{
await GenerateBittorentFileMovieAsync(user,movie);
});
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/movie/{movie}/extras?path={System.Web.HttpUtility.UrlEncode(parent)}");
}
}
}
private async Task ExtrasMovieMkdirAsync(ServerContext ctx)
{
ctx.ParseBody();
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(ctx.QueryParams.TryGetFirst("parent",out var parent) && ctx.QueryParams.TryGetFirst("name",out var name))
{
var _path = SanitizePath($"{parent.TrimStart('/').TrimEnd('/')}/{name}").TrimStart('/');
var _path2=Path.Combine(path,user,"movie",movie,"extras",_path);
if(me != null)
{
Directory.CreateDirectory(_path2);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/movie/{movie}/extras?path={System.Web.HttpUtility.UrlEncode(_path)}");
2023-12-16 01:40:05 +00:00
}
else
{
2024-01-04 02:53:13 +00:00
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/movie/{movie}/extras?path={System.Web.HttpUtility.UrlEncode(parent)}");
2023-12-16 01:40:05 +00:00
}
}
}
2024-01-04 02:53:13 +00:00
private async Task ExtrasMoviePageAsync(ServerContext ctx)
2023-12-16 01:40:05 +00:00
{
string user=usersPathValueServer.GetValue(ctx);
2024-01-04 02:53:13 +00:00
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
if(me != null && me.Username != user && !me.IsAdmin)
2023-12-16 01:40:05 +00:00
{
2024-01-04 02:53:13 +00:00
me=null;
}
if(!ctx.QueryParams.TryGetFirst("path",out var __path))
{
__path="/";
2023-12-16 01:40:05 +00:00
}
2024-01-04 02:53:13 +00:00
__path = __path.TrimStart('/');
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
__path=SanitizePath(__path);
string extrasPath = Path.Combine(path,user,"movie",movie,"extras");
if(__path.Length > 0)
{
extrasPath=Path.Combine(extrasPath,__path);
}
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
List<object> paths=new List<object>();
if(__path.Length > 0)
{
string up = Path.GetDirectoryName($"/{__path}").TrimStart('/');
string path = $"./extras?path=/{System.Web.HttpUtility.UrlEncode(up)}";
paths.Add(new {Path=path, Type="[PARENT]", Name="Up"});
}
else {
paths.Add(new {Path="./",Type="[PARENT]", Name="Up"});
}
foreach(var dir in Directory.EnumerateDirectories(extrasPath))
{
string dirname = Path.GetFileName(dir);
string dirname_html = System.Web.HttpUtility.HtmlEncode(dirname);
string path = $"./extras?path=/{System.Web.HttpUtility.UrlEncode(__path.TrimEnd('/') + '/' + dirname)}";
paths.Add(new {Path=path, Type="[DIR]", Name=dirname_html});
}
foreach(var file in Directory.EnumerateFiles(extrasPath))
{
string filename = Path.GetFileName(file);
string filename_html = System.Web.HttpUtility.HtmlEncode(filename);
string path = $"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/extras/{System.Web.HttpUtility.UrlPathEncode(__path.TrimEnd('/') + '/' + filename)}";
paths.Add(new {Path=path, Type="[FILE]", Name=filename_html});
}
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageExtrasViewer.RenderAsync(new{Extras=paths,Path=$"/{__path}",Parent=__path,Editable=me != null})));
}
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
private string SanitizePath(string path)
2023-12-16 01:40:05 +00:00
{
2024-01-04 02:53:13 +00:00
if(path.Length == 0) return "";
List<string> paths=new List<string>();
foreach(var item in path.Replace('\\','/').Split('/'))
{
if(item == "." || item == "..") continue;
paths.Add(item);
}
return string.Join("/",paths);
2023-12-16 01:40:05 +00:00
}
private class PieceStream : Stream
{
public const int PieceLength = 16*1024;
public int CurpieceOffset {get;private set;} =0;
public List<byte[]> Pieces {get;private set;}=new List<byte[]>();
public byte[] CalculateCurPiece()
{
using(var sha1=SHA1.Create())
{
return sha1.ComputeHash(Curpiece,0,CurpieceOffset);
}
}
public byte[] Curpiece = new byte[PieceLength];
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
long len;
public override long Length => len;
public bool HasPiece=>CurpieceOffset>0;
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override void Flush()
{
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
}
public override void Write(byte[] buffer, int offset, int count)
{
for(int i = 0;i<count;i++)
{
byte curByte = buffer[i+offset];
if(CurpieceOffset < PieceLength)
{
Curpiece[CurpieceOffset++] = curByte;
}
else
{
Pieces.Add(this.CalculateCurPiece());
CurpieceOffset=0;
Curpiece[CurpieceOffset++] = curByte;
}
}
len+=count;
}
}
private async Task GenerateBittorentFileMovieAsync(string user,string movie)
{
string movieDir = Path.Combine(this.path,user,"movie",movie);
Directory.CreateDirectory(movieDir);
string extrasDir = Path.Combine(movieDir,"extras");
string subtitles = Path.Combine(movieDir,"subtitles");
PieceStream strm = new PieceStream();
//calculate without extras
List<string[]> paths = new List<string[]>();
2024-01-04 02:53:13 +00:00
if(File.Exists(Path.Combine(movieDir,$"{movie}.mp4")))
2023-12-16 01:40:05 +00:00
paths.Add(new string[]{$"{movie}.mp4"});
2024-01-04 02:53:13 +00:00
if(File.Exists(Path.Combine(movieDir,"thumbnail.jpg")))
2023-12-16 01:40:05 +00:00
paths.Add(new string[]{"thumbnail.jpg"});
2024-01-04 02:53:13 +00:00
if(File.Exists(Path.Combine(movieDir,"poster.jpg")))
2023-12-16 01:40:05 +00:00
paths.Add(new string[]{"poster.jpg"});
if(Directory.Exists(subtitles))
GetExtras(paths,new string[]{"subtitles"},subtitles);
List<long> lengths = new List<long>();
foreach(var _path in paths.Select<string[],string>(e=>Path.Combine(e)))
using(var movieStrm = File.OpenRead(Path.Combine(movieDir,_path)))
{
lengths.Add(movieStrm.Length);
await movieStrm.CopyToAsync(strm);
}
Torrent torrent =new Torrent();
torrent.AnnounceList.AddRange(Configuration.BittorrentTrackers);
torrent.CreationDate=DateTime.Now;
torrent.UrlList = $"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/";
torrent.Info.PieceLength = PieceStream.PieceLength;
torrent.Info.Name = movie;
foreach(var piece in strm.Pieces)
{
torrent.Info.Pieces.Add(piece);
}
if(strm.HasPiece) torrent.Info.Pieces.Add(strm.CalculateCurPiece());
for(int i = 0;i<paths.Count;i++)
{
var _path = paths[i];
var len = lengths[i];
torrent.Info.Files.Add(new TorrentInfoFile(){Path=_path, Length = len});
}
string torrentFile = Path.Combine(movieDir,$"{movie}.torrent");
using(var file=File.Create(torrentFile))
{
await torrent.ToTorrentFile().WriteToStreamAsync(file);
}
if(Directory.Exists(extrasDir))
{
List<string[]> paths2 = new List<string[]>();
GetExtras(paths2,new string[]{"extras"},extrasDir);
foreach(var _path in paths2.Select<string[],string>(e=>Path.Combine(e)))
using(var movieStrm = File.OpenRead(Path.Combine(movieDir,_path)))
{
lengths.Add(movieStrm.Length);
await movieStrm.CopyToAsync(strm);
}
paths.AddRange(paths2);
torrent=new Torrent();
torrent.AnnounceList.AddRange(Configuration.BittorrentTrackers);
torrent.CreationDate=DateTime.Now;
torrent.UrlList = $"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/";
torrent.Info.PieceLength = PieceStream.PieceLength;
torrent.Info.Name = movie;
foreach(var piece in strm.Pieces)
{
torrent.Info.Pieces.Add(piece);
}
if(strm.HasPiece) torrent.Info.Pieces.Add(strm.CalculateCurPiece());
for(int i = 0;i<paths.Count;i++)
{
var _path = paths[i];
var len = lengths[i];
torrent.Info.Files.Add(new TorrentInfoFile(){Path=_path, Length = len});
}
torrentFile = Path.Combine(movieDir,$"{movie}_withextras.torrent");
using(var file=File.Create(torrentFile))
{
await torrent.ToTorrentFile().WriteToStreamAsync(file);
}
}
}
private void GetExtras(List<string[]> paths2, string[] torrentPath, string extrasDir)
{
foreach(var dir in Directory.GetDirectories(extrasDir))
{
string dirname = Path.GetFileName(dir);
string[] path = new string[torrentPath.Length+1];
Array.Copy(torrentPath,path,torrentPath.Length);
path[path.Length-1] = dirname;
GetExtras(paths2,path,dir);
}
foreach(var file in Directory.GetFiles(extrasDir))
{
string filename = Path.GetFileName(file);
string[] path = new string[torrentPath.Length+1];
Array.Copy(torrentPath,path,torrentPath.Length);
path[path.Length-1] = filename;
paths2.Add(path);
}
}
2024-01-04 02:53:13 +00:00
private async Task UploadSeasonStreamAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
string season = seasonPathValueServer.GetValue(ctx);
var _show= provider.GetShow(user,show);
var _user = provider.GetUserAccount(user);
if(!int.TryParse(season,out var seasonNo))
seasonNo=1;
var _season = provider.GetSeason(user,show,seasonNo);
var me = GetAccount(ctx);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
Directory.CreateDirectory(Path.Combine(path,user,"show",show,$"Season {seasonNo}"));
var tmpFile = Path.Combine(path,user,"show",show,$"Season {seasonNo}",$"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
foreach(var item in ctx.ParseBody((n,fn,ct)=>File.Create(tmpFile)))
item.Value.Dispose();
if(_show != null)
{
if(ctx.QueryParams.TryGetFirst("type",out var type))
{
switch(type)
{
case "thumbnail":
string thumb = Path.Combine(path,user,"show",show,$"Season {seasonNo}","thumbnail.jpg");
if(File.Exists(thumb)) File.Delete(thumb);
File.Move(tmpFile,thumb);
break;
case "poster":
string poster = Path.Combine(path,user,"show",show,$"Season {seasonNo}","poster.jpg");
if(File.Exists(poster)) File.Delete(poster);
File.Move(tmpFile,poster);
break;
}
ScheduleTask(async()=>{
await GenerateBittorentFileShowAsync(user,show);
});
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Success</h1><a href=\"./edit\">&lt;- Back</a>"));
return;
}
}
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Failed</h1><a href=\"./edit\">&lt;- Back</a>"));
}
private async Task UploadShowStreamAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string show=showPathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _show = provider.GetShow(user,show);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
Directory.CreateDirectory(Path.Combine(path,user,"show",show));
var tmpFile = Path.Combine(path,user,"show",show,$"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
foreach(var item in ctx.ParseBody((n,fn,ct)=>File.Create(tmpFile)))
item.Value.Dispose();
if(_show != null)
{
if(ctx.QueryParams.TryGetFirst("type",out var type))
{
switch(type)
{
case "thumbnail":
string thumb = Path.Combine(path,user,"show",show,"thumbnail.jpg");
if(File.Exists(thumb)) File.Delete(thumb);
File.Move(tmpFile,thumb);
break;
case "poster":
string poster = Path.Combine(path,user,"show",show,"poster.jpg");
if(File.Exists(poster)) File.Delete(poster);
File.Move(tmpFile,poster);
break;
}
ScheduleTask(async()=>{
await GenerateBittorentFileShowAsync(user,show);
});
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Success</h1><a href=\"./edit\">&lt;- Back</a>"));
return;
}
}
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Failed</h1><a href=\"./edit\">&lt;- Back</a>"));
}
private async Task GenerateBittorentFileShowAsync(string user, string show)
{
Console.WriteLine("Here");
string showDir = Path.Combine(this.path,user,"show",show);
Directory.CreateDirectory(showDir);
string extrasDir = Path.Combine(showDir,"extras");
PieceStream strm = new PieceStream();
//calculate without extras
List<string[]> paths = new List<string[]>();
for(int i = 1;i<=provider.SeasonCount(user,show);i++)
{
var season = provider.GetSeason(user,show,i);
if(season != null)
{
string season_name = $"Season {i}";
if(!Directory.Exists(Path.Combine(showDir,season_name))) continue;
if(File.Exists(Path.Combine(showDir,season_name,"thumbnail.jpg")))
paths.Add(new string[]{season_name,"thumbnail.jpg"});
if(File.Exists(Path.Combine(showDir,season_name,"poster.jpg")))
paths.Add(new string[]{season_name,"poster.jpg"});
for(int j = 1;j<=provider.EpisodeCount(user,show,i);j++)
{
var episode = provider.GetEpisode(user,show,i,j);
if(episode != null)
{
string name = $"{episode.EpisodeName} S{episode.SeasonNumber.ToString("D2")}E{episode.EpisodeNumber.ToString("D2")}";
string video_name = $"{name}.mp4";
string subtitles_name = $"{name}-subtitles";
string thumbnail = $"{name}-thumbnail.jpg";
string poster = $"{name}-poster.jpg";
if(File.Exists(Path.Combine(showDir,season_name,poster)))
paths.Add(new string[]{season_name,poster});
if(File.Exists(Path.Combine(showDir,season_name,thumbnail)))
paths.Add(new string[]{season_name,thumbnail});
if(File.Exists(Path.Combine(showDir,season_name,video_name)))
paths.Add(new string[]{season_name,video_name});
if(Directory.Exists(Path.Combine(showDir,season_name,subtitles_name)))
GetExtras(paths,new string[]{season_name,subtitles_name},Path.Combine(showDir,season_name,subtitles_name));
}
}
}
}
if(File.Exists(Path.Combine(showDir,"thumbnail.jpg")))
paths.Add(new string[]{"thumbnail.jpg"});
if(File.Exists(Path.Combine(showDir,"poster.jpg")))
paths.Add(new string[]{"poster.jpg"});
List<long> lengths = new List<long>();
foreach(var _path in paths.Select<string[],string>(e=>Path.Combine(e)))
using(var movieStrm = File.OpenRead(Path.Combine(showDir,_path)))
{
lengths.Add(movieStrm.Length);
await movieStrm.CopyToAsync(strm);
}
Torrent torrent =new Torrent();
torrent.AnnounceList.AddRange(Configuration.BittorrentTrackers);
torrent.CreationDate=DateTime.Now;
torrent.UrlList = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/";
torrent.Info.PieceLength = PieceStream.PieceLength;
torrent.Info.Name = show;
foreach(var piece in strm.Pieces)
{
torrent.Info.Pieces.Add(piece);
}
if(strm.HasPiece) torrent.Info.Pieces.Add(strm.CalculateCurPiece());
for(int i = 0;i<paths.Count;i++)
{
var _path = paths[i];
var len = lengths[i];
torrent.Info.Files.Add(new TorrentInfoFile(){Path=_path, Length = len});
}
string torrentFile = Path.Combine(showDir,$"{show}.torrent");
using(var file=File.Create(torrentFile))
{
await torrent.ToTorrentFile().WriteToStreamAsync(file);
}
if(Directory.Exists(extrasDir))
{
List<string[]> paths2 = new List<string[]>();
GetExtras(paths2,new string[]{"extras"},extrasDir);
foreach(var _path in paths2.Select<string[],string>(e=>Path.Combine(e)))
using(var movieStrm = File.OpenRead(Path.Combine(showDir,_path)))
{
lengths.Add(movieStrm.Length);
await movieStrm.CopyToAsync(strm);
}
paths.AddRange(paths2);
torrent=new Torrent();
torrent.AnnounceList.AddRange(Configuration.BittorrentTrackers);
torrent.CreationDate=DateTime.Now;
torrent.UrlList = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/";
torrent.Info.PieceLength = PieceStream.PieceLength;
torrent.Info.Name = show;
foreach(var piece in strm.Pieces)
{
torrent.Info.Pieces.Add(piece);
}
if(strm.HasPiece) torrent.Info.Pieces.Add(strm.CalculateCurPiece());
for(int i = 0;i<paths.Count;i++)
{
var _path = paths[i];
var len = lengths[i];
torrent.Info.Files.Add(new TorrentInfoFile(){Path=_path, Length = len});
}
torrentFile = Path.Combine(showDir,$"{show}_withextras.torrent");
using(var file=File.Create(torrentFile))
{
await torrent.ToTorrentFile().WriteToStreamAsync(file);
}
}
}
2023-12-16 01:40:05 +00:00
2024-01-04 02:53:13 +00:00
private async Task UploadMovieStreamAsync(ServerContext ctx)
2023-12-16 01:40:05 +00:00
{
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
Directory.CreateDirectory(Path.Combine(path,user,"movie",movie));
var tmpFile = Path.Combine(path,user,"movie",movie,$"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
foreach(var item in ctx.ParseBody((n,fn,ct)=>File.Create(tmpFile)))
item.Value.Dispose();
if(_movie != null)
{
if(ctx.QueryParams.TryGetFirst("type",out var type))
{
switch(type)
{
case "thumbnail":
2024-01-04 02:53:13 +00:00
string thumb = Path.Combine(path,user,"movie",movie,"thumbnail.jpg");
if(File.Exists(thumb)) File.Delete(thumb);
File.Move(tmpFile,thumb);
2023-12-16 01:40:05 +00:00
break;
case "poster":
2024-01-04 02:53:13 +00:00
string poster = Path.Combine(path,user,"movie",movie,"poster.jpg");
if(File.Exists(poster)) File.Delete(poster);
File.Move(tmpFile,poster);
2023-12-16 01:40:05 +00:00
break;
case "movie":
2024-01-04 02:53:13 +00:00
string mov=Path.Combine(path,user,"movie",movie,$"{movie}.mp4");
if(File.Exists(mov)) File.Delete(mov);
File.Move(tmpFile,mov);
ScheduleFFmpeg($"-y -i \"{mov}\" {Configuration.BrowserTranscode} \"{Path.Combine(path,user,"movie",movie,"browser.mp4")}\"");
2023-12-16 01:40:05 +00:00
break;
}
ScheduleTask(async()=>{
await GenerateBittorentFileMovieAsync(user,movie);
});
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Success</h1><a href=\"./edit\">&lt;- Back</a>"));
return;
}
}
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Failed</h1><a href=\"./edit\">&lt;- Back</a>"));
}
private void ScheduleFFmpeg(string command)
{
ScheduleTask(async()=>{
using(Process process=new Process())
{
process.StartInfo.Arguments = command;
process.StartInfo.FileName = "ffmpeg";
if(process.Start())
{
await Task.Run(process.WaitForExit);
}
}
});
}
public void ScheduleTask(Func<Task> task)
{
tasks.Enqueue(task);
}
2024-01-04 02:53:13 +00:00
public async Task EditMoviePagePostAsync(ServerContext ctx)
2023-12-16 01:40:05 +00:00
{
ctx.ParseBody();
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
if(_movie != null)
{
if(ctx.QueryParams.TryGetFirst("proper_name",out var proper_name) && ctx.QueryParams.TryGetFirst("description",out var description))
{
_movie.ProperName = proper_name;
2024-01-04 02:53:13 +00:00
_movie.Description = description.Replace("\r","");
2023-12-16 01:40:05 +00:00
provider.UpdateMovie(_movie);
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Success</h1><a href=\"./edit\">&lt;- Back</a>"));
return;
}
}
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>Failed</h1><a href=\"./edit\">&lt;- Back</a>"));
}
2024-01-04 02:53:13 +00:00
public async Task EditMoviePageAsync(ServerContext ctx)
2023-12-16 01:40:05 +00:00
{
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var me = GetAccount(ctx);
var _movie = provider.GetMovie(user,movie);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
if(me != null)
{
if(_movie != null)
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageEditMovieDetails.RenderAsync(new{Propername=System.Web.HttpUtility.HtmlAttributeEncode( _movie.ProperName),Description=System.Web.HttpUtility.HtmlEncode(_movie.Description)})));
}
else
{
await ctx.SendTextAsync(await RenderHtmlAsync(false,"<h1>You are unauthorized to edit this</h1>"));
}
}
private async Task PlayMoviePageAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var _movie= provider.GetMovie(user,movie);
object value;
if(_movie != null)
{
2024-01-04 02:53:13 +00:00
var data=GetMovieContentMetaData(Configuration,user,movie);
List<object> subtitles=new List<object>();
foreach(var subtitle in data.SubtitlesStreams)
{
string languageName = "Unknown";
string langCode = "";
foreach(var lang in Languages)
{
if(lang.LangCode == subtitle.LanguageCode)
{
languageName=lang.LangTitle;
langCode = lang.LangCodeVideo;
}
}
subtitles.Add(new {langcode=langCode,name=languageName,file=subtitle.VttUrl});
}
2023-12-16 01:40:05 +00:00
string thumb = data.ThumbnailUrl;
value = new{ username=user, rooturl=$"{Configuration.Root.TrimEnd('/')}/",
2024-01-04 02:53:13 +00:00
title=Configuration.Title,hasmovie=true,moviethumbnail=thumb,movieurl=movie,moviename=_movie.Name,moviedescription=_movie.Description,movieposter = data.PosterUrl, moviebrowserurl=data.BrowserStream,subtitles
};
2023-12-16 01:40:05 +00:00
}
else
{
value = new{ username=user, rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasmovie=false};
}
await ctx.SendTextAsync(await pageWatchMovie.RenderAsync(value));
}
private async Task MoviePageAsync(ServerContext ctx)
{
string user=usersPathValueServer.GetValue(ctx);
string movie=moviePathValueServer.GetValue(ctx);
var _movie= provider.GetMovie(user,movie);
var _user = provider.GetUserAccount(user);
var me = GetAccount(ctx);
if(me != null && me.Username != user && !me.IsAdmin)
{
me=null;
}
object value;
if(_movie != null && _user != null)
{
string movieDir = Path.Combine(this.path,user,"movie",movie);
bool torrent= File.Exists(Path.Combine(movieDir,$"{movie}.torrent"));
bool torrent_wextra= File.Exists(Path.Combine(movieDir,$"{movie}_withextras.torrent"));
2024-01-04 02:53:13 +00:00
bool extrasexists = Directory.Exists(Path.Combine(movieDir,"extras")) || me != null;
bool moviebrowserexists=File.Exists(Path.Combine(movieDir,"browser.mp4"));
bool movieexists=File.Exists(Path.Combine(movieDir,$"{movie}.mp4"));
string thumb = $"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/thumbnail.jpg";
value = new{ extrasexists=extrasexists,moviebrowserexists=moviebrowserexists,movieexists=movieexists, downloadurl=$"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.mp4",torrentexists=torrent, torrentwextraexists=torrent_wextra, torrent=$"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.torrent",torrentwextra=$"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}_withextras.torrent" , editable=me!=null, userproper=HttpUtility.HtmlEncode(_user.ProperName), username=HttpUtility.HtmlEncode(user), rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasmovie=true,moviethumbnail=thumb,movieurl=movie,movieproperattr=HttpUtility.HtmlAttributeEncode(_movie.ProperName),movieproper=HttpUtility.HtmlEncode(_movie.ProperName),moviename=HttpUtility.HtmlEncode(_movie.Name),moviedescription=DescriptLinkUtils(_movie.Description ?? "").Replace("\n","<br>")};
2023-12-16 01:40:05 +00:00
}
else
{
value = new{ username=user, rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasmovie=false};
}
await ctx.SendTextAsync(await RenderHtmlAsync(false,await pageMovie.RenderAsync(value)));
}
private IServer CreateSwagme()
{
SwagmeServer swagmeServer=new SwagmeServer();
swagmeServer.AbsoluteUrl=true;
swagmeServer.Add("/MovieFile",ApiMovieFileAsync,new SwagmeDocumentation("Get the movie resource"),"GET","Movies");
2024-01-04 02:53:13 +00:00
swagmeServer.Add("/GetMovies",ApiGetMoviesAsync,new SwagmeDocumentation("Get a list of movies","<b>user</b>: the user of the movie<br><b>type</b>: format of list (defaults to json): pls, wiimc, json, m3u8 or rss"),"GET","Movies");
2023-12-16 01:40:05 +00:00
return swagmeServer;
}
private async Task ApiMovieFileAsync(ServerContext ctx)
{
if(ctx.QueryParams.TryGetFirst("user",out var user) && ctx.QueryParams.TryGetFirst("movie", out var movie))
{
if(!ctx.QueryParams.TryGetFirst("type", out var type))
type="download";
2024-01-04 02:53:13 +00:00
var info =GetMovieContentMetaData(Configuration,user,movie);
2023-12-16 01:40:05 +00:00
switch(type)
{
case "download":
await ctx.SendRedirectAsync( info.DownloadStream);
break;
case "browser":
await ctx.SendRedirectAsync(info.BrowserStream);
break;
case "thumbnail":
await ctx.SendRedirectAsync(info.ThumbnailUrl);
break;
case "poster":
await ctx.SendRedirectAsync(info.PosterUrl);
break;
case "torrent":
await ctx.SendRedirectAsync(info.MovieTorrentUrl);
break;
case "torrent_extra":
await ctx.SendRedirectAsync(info.MovieWithExtrasTorrentUrl);
break;
case "json":
await ctx.SendJsonAsync(info);
break;
}
}
}
private async Task ApiGetMoviesAsync(ServerContext ctx)
{
UserAccount a;
if(!ctx.QueryParams.TryGetFirst("type",out var type)) type="json";
if(ctx.QueryParams.TryGetFirst("user",out var user))
{
a = provider.GetUserAccount(user);
}
else
{
a = provider.GetFirstUser();
2024-01-04 02:53:13 +00:00
user = a.Username;
2023-12-16 01:40:05 +00:00
}
List<Movie> movies=new List<Movie>();
if(a != null)
{
movies.AddRange(provider.GetMovies(a.Id));
}
if(type=="json")
{
await ctx.SendJsonAsync(movies);
}
2024-01-04 02:53:13 +00:00
if(type == "pls" || type == "wiimc")
2023-12-16 01:40:05 +00:00
{
StringBuilder b=new StringBuilder();
b.AppendLine("[Playlist]");
int i = 1;
foreach(var item in movies)
{
2024-01-04 02:53:13 +00:00
if(type=="wiimc")
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/content/{HttpUtility.UrlEncode(user)}/movie/{item.Name}/browser.mp4");
else
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/api/v1/MovieFile?user={HttpUtility.UrlEncode(user)}&movie={HttpUtility.UrlEncode(item.Name)}&type=download");
2023-12-16 01:40:05 +00:00
b.AppendLine($"Length{i}=0");
b.AppendLine($"Title{i}={IniEscape(item.ProperName)}");
i++;
}
await ctx.SendTextAsync(b.ToString(),"audio/x-scpls");
}
if(type == "m3u8")
{
M3uPlaylist playlist=new M3uPlaylist();
foreach(var item in movies)
{
M3uPlaylistEntry entry=new M3uPlaylistEntry();
entry.Path = $"{Configuration.Root.TrimEnd('/')}/api/v1/MovieFile?user={HttpUtility.UrlEncode(user)}&movie={HttpUtility.UrlEncode(item.Name)}&type=download";
entry.Title = item.ProperName;
playlist.PlaylistEntries.Add(entry);
}
M3uContent content = new M3uContent();
await ctx.SendTextAsync(content.ToText(playlist),"application/x-mpegurl");
}
if(type == "rss")
{
StringWriter sw = new StringWriter();
using (XmlWriter xmlWriter = XmlWriter.Create(sw, new XmlWriterSettings() { Async = true, Indent = false,OmitXmlDeclaration=true, Encoding= Encoding.UTF8 }))
{
var rss = new RssFeedWriter(xmlWriter);
await rss.WriteTitle($"{a.ProperName}'s Movies");
await rss.WriteGenerator("TessesCMS");
await rss.WriteValue("link",$"{Configuration.Root.TrimEnd('/')}/");
foreach(var item in movies)
{
AtomEntry entry=new AtomEntry();
entry.Title = item.ProperName;
entry.Description = $"View <a href=\"{Configuration.Root.TrimEnd('/')}/user/{user}/movie/{HttpUtility.UrlEncode(item.Name)}/\">here</a><br>{item.Description}";
entry.LastUpdated = item.LastUpdated;
entry.Published = item.CreationTime;
await rss.Write(entry);
}
}
await ctx.SendTextAsync(sw.GetStringBuilder().ToString(),"application/rss+xml");
}
}
private string IniEscape(string properName)
{
StringBuilder b=new StringBuilder();
foreach(var c in properName)
{
if(c == '\\' || c ==';' || c == '\"' || c == '\'' || c == '#' || c == '=' || c == ':')
{
b.Append($"\\{c}");
}
else if(c == '\0')
{
b.Append("\\0");
}
else if(c == '\b')
{
b.Append("\\b");
}
else if(c == '\a')
{
b.Append("\\a");
}
else if(c == '\t')
{
b.Append("\\t");
}
else if(c == '\r')
{
b.Append("\\r");
}else if(c == '\n')
{
b.Append("\\n");
}
else if(c > sbyte.MaxValue)
{
b.Append($"\\x{((int)c).ToString("X4")}");
}
else {
b.Append(c);
}
}
return b.ToString();
}
private async Task<string> RenderHtmlAsync(bool isHome,string body,params CMSNavUrl[] urls)
{
List<object> _urls = new List<object>();
foreach(var item in urls)
{
_urls.Add(new{text=item.Text, url=item.Url,active=item.Active});
}
foreach(var item in Configuration.Urls)
{
_urls.Add(new{text=item.Text,url=item.Url,active=item.Active});
}
return await pageShell.RenderAsync(new{
ishome = isHome,
body = body,
urls=_urls,
rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title
});
}
private async Task Index(ServerContext ctx)
{
var account=GetAccount(ctx);
CMSNavUrl accountLink=account != null ? Configuration.RelativeNavUrl(account.ProperName,"account") : Configuration.RelativeNavUrl("Login","login");
await ctx.SendTextAsync(await RenderHtmlAsync(true,await RenderIndexAsync(),Configuration.RelativeNavUrl("Devcenter","devcenter"), accountLink));
}
private async Task Devcenter(ServerContext ctx)
{
var link = Configuration.RelativeNavUrl("Devcenter","devcenter");
link.Active=true;
await ctx.SendTextAsync(await RenderHtmlAsync(false,await RenderDevcenterAsync(),link));
}
private async Task<string> RenderDevcenterAsync()
{
return await pageDevcenter.RenderAsync(new{title=Configuration.Title,rooturl=$"{Configuration.Root.TrimEnd('/')}/"});
}
private async Task<string> RenderIndexAsync()
{
return await pageIndex.RenderAsync(new{title=Configuration.Title,rooturl=$"{Configuration.Root.TrimEnd('/')}/"});
}
public async Task<string> RenderUpload1Async()
{
return await pageUpload.RenderAsync(new{title=Configuration.Title,rooturl=$"{Configuration.Root.TrimEnd('/')}/"});
}
public CMSConfiguration Configuration {get;set;}=new CMSConfiguration();
public IServer Server => routeServer;
}
2024-01-04 02:53:13 +00:00
internal class EmailCreator
{
CMSConfiguration configuration;
Template emailTemplate;
Template verifyTemplate;
public EmailCreator(CMSConfiguration configuration)
{
this.configuration = configuration;
emailTemplate = Template.Parse(AssetProvider.ReadAllText("/EmailHtml.html"));
verifyTemplate = Template.Parse(AssetProvider.ReadAllText("/VerifyEmail.html"));
}
private async Task SendEmailAsync(string email,string emailHtml,string subject)
{
if(InternetAddress.TryParse(email,out var to) && InternetAddress.TryParse(this.configuration.Email.Email,out var from))
using(var smtp=new SmtpClient())
{
await smtp.ConnectAsync(configuration.Email.Host,configuration.Email.Port,configuration.Email.Encryption);
await smtp.AuthenticateAsync(configuration.Email.User,configuration.Email.Pass);
MimeMessage message=new MimeMessage();
message.From.Add(from);
message.To.Add(to);
message.Body = new TextPart(MimeKit.Text.TextFormat.Html){Text=await emailTemplate.RenderAsync(new{Body=emailHtml,Websitename=HttpUtility.HtmlEncode(configuration.Title),Websiteurl=HttpUtility.HtmlAttributeEncode($"{configuration.Root.TrimEnd('/')}/")})};
message.Subject = subject;
await smtp.SendAsync(message);
}
}
public async Task SendVerificationEmailAsync(UserAccount account,string verificationCode)
{
string verifyLink = $"{configuration.Root.TrimEnd('/')}/verify?token={HttpUtility.UrlEncode(verificationCode)}";
await SendEmailAsync(account.Email,await verifyTemplate.RenderAsync(new{verifyurllink=HttpUtility.HtmlAttributeEncode(verifyLink),verifyurl=HttpUtility.HtmlEncode(verifyLink),Propername=HttpUtility.HtmlEncode(account.ProperName)}),$"Verify email for {configuration.Title}");
}
}
2023-12-16 01:40:05 +00:00
}