tesses-cms/Tesses.CMS/Class1.cs

935 lines
39 KiB
C#

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;
namespace Tesses.CMS
{
public class CMSServer
{
public Dictionary<string,UserAccount> Sessions {get;set;}=new Dictionary<string, UserAccount>();
Template pageShell;
Template pageIndex;
Template pageDevcenter;
Template pageMovie;
Template pageMovies;
Template pageWatchMovie;
Template pageUpload;
Template pageUsers;
Template pageEditMovieDetails;
RouteServer routeServer;
PathValueServer usersPathValueServer;
PathValueServer moviePathValueServer;
MountableServer usersMountableServer;
RouteServer movieRouteServer;
IContentProvider provider;
string path;
public CMSServer(string configpath,IContentProvider provider)
{
this.provider=provider;
usersMountableServer=new MountableServer();
usersPathValueServer = new PathValueServer(usersMountableServer);
movieRouteServer=new RouteServer();
moviePathValueServer = new PathValueServer(movieRouteServer);
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"
});
}
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"));
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);
routeServer.Add("/login",LoginPostAsync,"POST");
routeServer.Add("/signup",SignupAsync);
routeServer.Add("/signup",SignupPostAsync,"POST");
RegisterUsersPath();
Task.Factory.StartNew(async()=>{
while(Running)
{
if(tasks.TryDequeue(out var item))
{
await item();
}
await Task.Delay(TimeSpan.FromSeconds(0.125));
}
}).Wait(0);
}
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);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
}
}
}
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);
} while(Sessions.ContainsKey(cookie));
Sessions.Add(cookie,a);
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>");
}
}
private UserAccount GetAccount(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(Sessions.TryGetValue(co[1],out var account))
return account;
}
}
}
return null;
}
private async Task UsersAsync(ServerContext ctx)
{
List<object> users=new List<object>();
foreach(var user in provider.GetUsers())
{
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;
}
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":
provider.CreateMovie(account.Username,name,proper_name,description);
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{account.Username}/movie/{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 = provider.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);
usersMountableServer.Mount("/",routeServer);
usersMountableServer.Mount("/movies/",Movies());
usersMountableServer.Mount("/movie/",moviePathValueServer);
RegisterMoviePath();
}
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",EditPageAsync);
movieRouteServer.Add("/edit",EditPagePostAsync,"POST");
movieRouteServer.Add("/upload",UploadStreamAsync,"POST");
}
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);
Console.WriteLine($"Starting torrent creation: {movieDir}");
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[]>();
paths.Add(new string[]{$"{movie}.mp4"});
paths.Add(new string[]{"thumbnail.jpg"});
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");
Console.WriteLine(torrentFile);
using(var file=File.Create(torrentFile))
{
await torrent.ToTorrentFile().WriteToStreamAsync(file);
}
Console.WriteLine($"Created Torrent: {torrentFile}");
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);
}
}
private async Task UploadStreamAsync(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");
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":
File.Move(tmpFile,Path.Combine(path,user,"movie",movie,"thumbnail.jpg"));
break;
case "poster":
File.Move(tmpFile,Path.Combine(path,user,"movie",movie,"poster.jpg"));
break;
case "movie":
File.Move(tmpFile,Path.Combine(path,user,"movie",movie,$"{movie}.mp4"));
ScheduleFFmpeg($"-i \"{Path.Combine(path,user,"movie",movie,$"{movie}.mp4")}\" {Configuration.BrowserTranscode} \"{Path.Combine(path,user,"movie",movie,"browser.mp4")}\"");
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);
}
public async Task EditPagePostAsync(ServerContext ctx)
{
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;
_movie.Description = description;
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>"));
}
public async Task EditPageAsync(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(_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)
{
var data=provider.GetMovieContentMetaData(Configuration,user,movie);
string thumb = data.ThumbnailUrl;
value = new{ username=user, rooturl=$"{Configuration.Root.TrimEnd('/')}/",
title=Configuration.Title,hasmovie=true,moviethumbnail=thumb,movieurl=movie,moviename=_movie.Name,moviedescription=_movie.Description,movieposter = data.PosterUrl, moviebrowserurl=data.BrowserStream};
}
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)
{
var data=provider.GetMovieContentMetaData(Configuration,user,movie);
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"));
string thumb = data.ThumbnailUrl;
value = new{ downloadurl=data.DownloadStream,torrentexists=torrent, torrentwextraexists=torrent_wextra, torrent=data.MovieTorrentUrl,torrentwextra=data.MovieWithExtrasTorrentUrl , 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=HttpUtility.HtmlEncode(_movie.Description)};
}
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");
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, json, m3u8 or rss"),"GET","Movies");
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";
var info = provider.GetMovieContentMetaData(Configuration,user,movie);
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();
}
List<Movie> movies=new List<Movie>();
if(a != null)
{
movies.AddRange(provider.GetMovies(a.Id));
}
if(type=="json")
{
await ctx.SendJsonAsync(movies);
}
if(type == "pls")
{
StringBuilder b=new StringBuilder();
b.AppendLine("[Playlist]");
int i = 1;
foreach(var item in movies)
{
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/api/v1/MovieFile?user={HttpUtility.UrlEncode(user)}&movie={HttpUtility.UrlEncode(item.Name)}&type=download");
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;
}
}