5493 lines
250 KiB
C#
5493 lines
250 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;
|
|
using MailKit.Net.Smtp;
|
|
using MimeKit;
|
|
using System.Net.Http;
|
|
using System.Runtime.Serialization;
|
|
namespace Tesses.CMS
|
|
{
|
|
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);
|
|
|
|
writer.WriteLine($"{begin.ToString("hh\\:mm\\:ss\\.fff")} --> {end.ToString("hh\\:mm\\:ss\\.fff")}");
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
public class CMSServer
|
|
{
|
|
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 ShowContentMetaData GetShowContentMetaData(string user, string show)
|
|
{
|
|
string showDir = Path.Combine(this.path, user, "show", show);
|
|
var _show = new ShowContentMetaData()
|
|
{
|
|
Info = provider.GetShow(user, show),
|
|
HasPoster = File.Exists(Path.Combine(path, user, "show", show, "poster.jpg")),
|
|
HasThumbnail = File.Exists(Path.Combine(path, user, "show", show, "thumbnail.jpg")),
|
|
HasShowTorrent = File.Exists(Path.Combine(path, user, "show", show, $"{show}.torrent")),
|
|
HasShowWithExtrasTorrent = File.Exists(Path.Combine(path, user, "show", show, $"{show}_withextras.torrent")),
|
|
PosterUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/poster.jpg",
|
|
ThumbnailUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg",
|
|
ShowTorrentUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/{show}.torrent",
|
|
ShowWithExtrasTorrentUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/{show}_withextras.torrent"
|
|
};
|
|
string extrasPath = Path.Combine(path, user, "show", show, "extras");
|
|
if (Directory.Exists(extrasPath))
|
|
FindExtras($"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/extras/", extrasPath, _show.ExtraStreams);
|
|
|
|
return _show;
|
|
}
|
|
public SeasonContentMetaData GetSeasonContentMetaData(string user, string show, int season)
|
|
{
|
|
string showDir = Path.Combine(this.path, user, "show", show);
|
|
string episodeDir = Path.Combine(showDir, $"Season {season.ToString("D2")}");
|
|
var _season0 = new SeasonContentMetaData()
|
|
{
|
|
Info = provider.GetSeason(user, show, season),
|
|
HasPoster = File.Exists(Path.Combine(episodeDir, "poster.jpg")) ? true : File.Exists(Path.Combine(this.path, user, "show", show, "poster.jpg")),
|
|
HasThumbnail = File.Exists(Path.Combine(episodeDir, "thumbnail.jpg")) ? true : File.Exists(Path.Combine(this.path, user, "show", show, "thumbnail.jpg")),
|
|
PosterUrl = File.Exists(Path.Combine(episodeDir, "poster.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/poster.jpg" : $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/poster.jpg",
|
|
ThumbnailUrl = File.Exists(Path.Combine(episodeDir, "thumbnail.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/thumbnail.jpg" : $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg",
|
|
};
|
|
|
|
return _season0;
|
|
}
|
|
public EpisodeContentMetaData GetEpisodeContentMetaData(string user, string show, int season, int episode)
|
|
{
|
|
string episodeDir = Path.Combine(this.path, user, "show", show, $"Season {season.ToString("D2")}");
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
string name = $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}";
|
|
var _episode0 = new EpisodeContentMetaData()
|
|
{
|
|
HasPoster = File.Exists(Path.Combine(episodeDir, $"{name}-poster.jpg")) ? true : (File.Exists(Path.Combine(episodeDir, "poster.jpg")) ? true : File.Exists(Path.Combine(this.path, user, "show", show, "poster.jpg"))),
|
|
HasThumbnail = File.Exists(Path.Combine(episodeDir, $"{name}-thumbnail.jpg")) ? true : (File.Exists(Path.Combine(episodeDir, "thumbnail.jpg")) ? true : File.Exists(Path.Combine(this.path, user, "show", show, "thumbnail.jpg"))),
|
|
HasBrowserStream = File.Exists(Path.Combine(episodeDir, $"S{season.ToString("D2")}E{episode.ToString("D2")}.mp4")),
|
|
HasDownloadStream = File.Exists(Path.Combine(episodeDir, $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}.mp4")),
|
|
PosterUrl = File.Exists(Path.Combine(episodeDir, $"{name}-poster.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/{_episode.EpisodeName}%20S{season.ToString("D2")}E{episode.ToString("D2")}-poster.jpg" : File.Exists(Path.Combine(episodeDir, "poster.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/poster.jpg" : $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/poster.jpg",
|
|
ThumbnailUrl = File.Exists(Path.Combine(episodeDir, $"{name}-thumbnail.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/{_episode.EpisodeName}%20S{season.ToString("D2")}E{episode.ToString("D2")}-thumbnail.jpg" : File.Exists(Path.Combine(episodeDir, "thumbnail.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/thumbnail.jpg" : $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg",
|
|
BrowserStream = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/S{season.ToString("D2")}E{episode.ToString("D2")}.mp4",
|
|
DownloadStream = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/{_episode.EpisodeName}%20S{season.ToString("D2")}E{episode.ToString("D2")}.mp4",
|
|
Info = provider.GetEpisode(user, show, season, episode)
|
|
};
|
|
|
|
GetEpisodeSubtitleStreams(_episode0.SubtitlesStreams, user, show, season, episode, _episode.EpisodeName);
|
|
|
|
return _episode0;
|
|
}
|
|
public void GetEpisodeSubtitleStreams(List<SubtitleStream> subtitleStreams, string user, string show, int season, int episode, string episode_name)
|
|
{
|
|
|
|
string subDir = Path.Combine(path, user, "show", show, $"Season {season.ToString("D2")}", $"{episode_name} S{season.ToString("D2")}E{episode.ToString("D2")}-subtitles");
|
|
|
|
GetSubtitleStreams(subtitleStreams, subDir, $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/{episode_name}%20S{season.ToString("D2")}E{episode.ToString("D2")}-subtitles", $"{episode_name}%20S{season.ToString("D2")}E{episode.ToString("D2")}");
|
|
}
|
|
public void GetMovieSubtitleStreams(List<SubtitleStream> subtitleStreams, string user, string movie)
|
|
{
|
|
string subDir = Path.Combine(path, user, "movie", movie, "subtitles");
|
|
GetSubtitleStreams(subtitleStreams, subDir, $"{Configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/subtitles", movie);
|
|
}
|
|
public void GetSubtitleStreams(List<SubtitleStream> subtitleStreams, string subDir, string subUrl, string file)
|
|
{
|
|
|
|
if (Directory.Exists(subDir))
|
|
foreach (var language in Directory.GetDirectories(subDir))
|
|
{
|
|
string languageCode = Path.GetFileName(language); //en-US for english
|
|
subtitleStreams.Add(new SubtitleStream() { LanguageCode = languageCode, VttUrl = $"{subUrl}/{languageCode}/{file}.vtt", SrtUrl = $"{subUrl}/{languageCode}/{file}.srt" });
|
|
}
|
|
|
|
}
|
|
public AlbumContentMetaData GetAlbumContentMetadata(string user, string album)
|
|
{
|
|
var _albumMeta = provider.GetAlbum(user, album);
|
|
List<Track> browserStreams = new List<Track>();
|
|
List<Track> downloadStreams = new List<Track>();
|
|
if (_albumMeta != null)
|
|
{
|
|
int trackNumber = 1;
|
|
foreach (var track in _albumMeta.Tracks)
|
|
{
|
|
string baseurl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}";
|
|
string browserurl = $"{baseurl}/{HttpUtility.UrlPathEncode(track)}.mp3";
|
|
string downloadurl = $"{baseurl}/{trackNumber.ToString("D2")}%20{HttpUtility.UrlPathEncode(_albumMeta.AlbumArtist)}%20-%20{HttpUtility.UrlPathEncode(track)}.flac";
|
|
|
|
if (File.Exists(Path.Combine(path, user, "album", album, $"{track}.mp3")))
|
|
browserStreams.Add(new Track { Url = browserurl, Name = track, TrackNumber = trackNumber });
|
|
|
|
if (File.Exists(Path.Combine(path, user, "album", album, $"{trackNumber.ToString("D2")} {_albumMeta.AlbumArtist} - {track}.flac")))
|
|
downloadStreams.Add(new Track { Url = downloadurl, Name = track, TrackNumber = trackNumber });
|
|
|
|
trackNumber++;
|
|
}
|
|
}
|
|
|
|
var _album = new AlbumContentMetaData()
|
|
{
|
|
HasPoster = File.Exists(Path.Combine(path, user, "album", album, "poster.jpg")),
|
|
HasThumbnail = File.Exists(Path.Combine(path, user, "album", album, "thumbnail.jpg")),
|
|
HasAlbumTorrent = File.Exists(Path.Combine(path, user, "album", album, $"{album}.torrent")),
|
|
HasAlbumWithExtrasTorrent = File.Exists(Path.Combine(path, user, "album", album, $"{album}_withextras.torrent")),
|
|
PosterUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/poster.jpg",
|
|
ThumbnailUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/thumbnail.jpg",
|
|
AlbumTorrentUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/{album}.torrent",
|
|
AlbumWithExtrasTorrentUrl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/{album}_withextras.torrent",
|
|
DownloadStreams = downloadStreams,
|
|
BrowserStreams = browserStreams,
|
|
Info = _albumMeta
|
|
};
|
|
string extrasPath = Path.Combine(path, user, "album", album, "extras");
|
|
if (Directory.Exists(extrasPath))
|
|
FindExtras($"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/extras/", extrasPath, _album.ExtraStreams);
|
|
return _album;
|
|
}
|
|
|
|
|
|
public MovieContentMetaData GetMovieContentMetaData(string user, string movie)
|
|
{
|
|
var _movie = new MovieContentMetaData()
|
|
{
|
|
HasPoster = File.Exists(Path.Combine(path, user, "movie", movie, "poster.jpg")),
|
|
HasThumbnail = File.Exists(Path.Combine(path, user, "movie", movie, "thumbnail.jpg")),
|
|
HasBrowserStream = File.Exists(Path.Combine(path, user, "movie", movie, "browser.mp4")),
|
|
HasDownloadStream = File.Exists(Path.Combine(path, user, "movie", movie, $"{movie}.mp4")),
|
|
HasMovieTorrent = File.Exists(Path.Combine(path, user, "movie", movie, $"{movie}.torrent")),
|
|
HasMovieWithExtrasTorrent = File.Exists(Path.Combine(path, user, "movie", movie, $"{movie}_withextras.torrent")),
|
|
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"
|
|
};
|
|
GetMovieSubtitleStreams(_movie.SubtitlesStreams, user, movie);
|
|
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; }
|
|
|
|
Template pageShell;
|
|
Template pageIndex;
|
|
Template pageDevcenter;
|
|
Template pageShow;
|
|
Template pageSeason;
|
|
Template pageEpisode;
|
|
Template pageShows;
|
|
Template pageMovie;
|
|
Template pageAlbum;
|
|
Template pageMusicPlayer;
|
|
private Template pageAlbums;
|
|
Template pageMovies;
|
|
Template pageWatchMovie;
|
|
Template pageWatchEpisode;
|
|
|
|
Template pageUpload;
|
|
|
|
Template pageUsers;
|
|
Template pageEditMovieDetails;
|
|
Template pageEditShowDetails;
|
|
Template pageEditSeasonDetails;
|
|
Template pageEditEpisodeDetails;
|
|
Template pageEditAlbumDetails;
|
|
|
|
Template pageExtrasViewer;
|
|
Template pageEditUser;
|
|
private Template pageTracklist;
|
|
Template aboutUser;
|
|
Template pageSubtitleLangList;
|
|
|
|
Template pageSubtitleEditor;
|
|
|
|
Template pageMailingList;
|
|
Template pageVerifyEmail;
|
|
Template pageManage;
|
|
|
|
Template pageEmailMovie;
|
|
Template pageWebhook;
|
|
private Template siteWebMan;
|
|
RouteServer routeServer;
|
|
|
|
|
|
|
|
PathValueServer usersPathValueServer;
|
|
|
|
PathValueServer moviePathValueServer;
|
|
|
|
PathValueServer albumPathValueServer;
|
|
|
|
PathValueServer showPathValueServer;
|
|
PathValueServer seasonPathValueServer;
|
|
PathValueServer episodePathValueServer;
|
|
|
|
MountableServer showMountableServer;
|
|
MountableServer usersMountableServer;
|
|
MountableServer seasonMountableServer;
|
|
|
|
|
|
|
|
RouteServer movieRouteServer;
|
|
|
|
RouteServer showRouteServer;
|
|
RouteServer seasonRouteServer;
|
|
|
|
RouteServer episodeRouteServer;
|
|
private RouteServer albumRouteServer;
|
|
IContentProvider provider;
|
|
string path;
|
|
private event Action<object> SendEvents;
|
|
public CMSServer(string configpath, IContentProvider provider)
|
|
{
|
|
this.provider = provider;
|
|
usersMountableServer = new MountableServer();
|
|
usersPathValueServer = new PathValueServer(usersMountableServer);
|
|
movieRouteServer = new RouteServer();
|
|
showRouteServer = new RouteServer();
|
|
seasonRouteServer = new RouteServer();
|
|
episodeRouteServer = new RouteServer();
|
|
albumRouteServer = new RouteServer();
|
|
moviePathValueServer = new PathValueServer(movieRouteServer);
|
|
showMountableServer = new MountableServer(showRouteServer);
|
|
showPathValueServer = new PathValueServer(showMountableServer);
|
|
albumPathValueServer = new PathValueServer(albumRouteServer);
|
|
seasonMountableServer = new MountableServer(seasonRouteServer);
|
|
seasonPathValueServer = new PathValueServer(seasonMountableServer);
|
|
episodePathValueServer = new PathValueServer(episodeRouteServer);
|
|
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"
|
|
});
|
|
}
|
|
Creator = new EmailCreator(Configuration);
|
|
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"));
|
|
pageAlbum = Template.Parse(AssetProvider.ReadAllText("/AlbumPage.html"));
|
|
pageMusicPlayer = Template.Parse(AssetProvider.ReadAllText("/MusicPlayerPage.html"));
|
|
pageAlbums = Template.Parse(AssetProvider.ReadAllText("/AlbumsPage.html"));
|
|
pageMovies = Template.Parse(AssetProvider.ReadAllText("/MoviesPage.html"));
|
|
pageUsers = Template.Parse(AssetProvider.ReadAllText("/UsersPage.html"));
|
|
pageWatchMovie = Template.Parse(AssetProvider.ReadAllText("/WatchMovie.html"));
|
|
pageWatchEpisode = Template.Parse(AssetProvider.ReadAllText("/WatchEpisode.html"));
|
|
pageUpload = Template.Parse(AssetProvider.ReadAllText("/Upload.html"));
|
|
pageEditMovieDetails = Template.Parse(AssetProvider.ReadAllText("/EditMovieDetails.html"));
|
|
pageEditShowDetails = Template.Parse(AssetProvider.ReadAllText("/EditShowDetails.html"));
|
|
pageEditSeasonDetails = Template.Parse(AssetProvider.ReadAllText("/EditSeasonDetails.html"));
|
|
pageEditEpisodeDetails = Template.Parse(AssetProvider.ReadAllText("/EditEpisodeDetails.html"));
|
|
pageEditAlbumDetails = Template.Parse(AssetProvider.ReadAllText("/EditAlbumDetails.html"));
|
|
pageExtrasViewer = Template.Parse(AssetProvider.ReadAllText("/ExtrasViewer.html"));
|
|
pageEditUser = Template.Parse(AssetProvider.ReadAllText("/AccountEditor.html"));
|
|
pageTracklist = Template.Parse(AssetProvider.ReadAllText("/Tracklist.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"));
|
|
pageEpisode = Template.Parse(AssetProvider.ReadAllText("/EpisodePage.html"));
|
|
pageShows = Template.Parse(AssetProvider.ReadAllText("/ShowsPage.html"));
|
|
pageMailingList = Template.Parse(AssetProvider.ReadAllText("/MailingList.html"));
|
|
pageEmailMovie = Template.Parse(AssetProvider.ReadAllText("/EmailMovie.html"));
|
|
pageWebhook = Template.Parse(AssetProvider.ReadAllText("/Webhook.html"));
|
|
siteWebMan = Template.Parse("/site.webmanifest");
|
|
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("/site.webmanifest", SiteWebManifestAsync, "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("/logout", LogoutAsync);
|
|
routeServer.Add("/login", LoginPostAsync, "POST");
|
|
routeServer.Add("/signup", SignupAsync);
|
|
routeServer.Add("/signup", SignupPostAsync, "POST");
|
|
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);
|
|
|
|
RegisterUsersPath();
|
|
Task.Factory.StartNew(async () =>
|
|
{
|
|
Stopwatch watch = new Stopwatch();
|
|
watch.Start();
|
|
while (Running)
|
|
{
|
|
if (tasks.TryDequeue(out var item))
|
|
{
|
|
await item();
|
|
}
|
|
await Task.Delay(TimeSpan.FromSeconds(0.125));
|
|
if (watch.Elapsed.TotalMinutes > 10)
|
|
{
|
|
ClearExpiredCSRF();
|
|
watch.Restart();
|
|
}
|
|
}
|
|
}).Wait(0);
|
|
}
|
|
|
|
private void ClearExpiredCSRF()
|
|
{
|
|
lock (CsrfTokens)
|
|
{
|
|
List<CSRF> csrfs = new List<CSRF>();
|
|
var dtNow = DateTime.Now;
|
|
foreach (var item in CsrfTokens)
|
|
{
|
|
if (item.Expires < dtNow) csrfs.Add(item);
|
|
}
|
|
foreach (var item in csrfs)
|
|
{
|
|
CsrfTokens.Remove(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task SiteWebManifestAsync(ServerContext ctx)
|
|
{
|
|
await ctx.SendTextAsync(await siteWebMan.RenderAsync(new
|
|
{
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title
|
|
}), "application/manifest+json");
|
|
}
|
|
|
|
public async Task ManagePostAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
var account = GetAccount(ctx, true);
|
|
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, true);
|
|
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, out var cookie, false); 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 csrf = "";
|
|
|
|
string csrf2 = "";
|
|
if(account != null)
|
|
{
|
|
csrf=HttpUtility.UrlEncode(CreateCSRF(account.Id, cookie));
|
|
csrf2=HttpUtility.HtmlAttributeEncode(CreateCSRF(account.Id, cookie));
|
|
}
|
|
string impersonate = $"{Configuration.Root.TrimEnd('/')}/impersonate?user={user.Username}&csrf={csrf}";
|
|
users.Add(new { csrf = csrf2, 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(ctx, 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(ctx, 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(ctx, "<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(ctx, "<h1>Failed to verify account</h1>"));
|
|
}
|
|
}
|
|
|
|
private async Task AccountPostAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
var account = GetAccount(ctx,true);
|
|
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(ctx, 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('/')}/");
|
|
}
|
|
|
|
public bool Running = true;
|
|
public async Task LoginAsync(ServerContext ctx)
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await AssetProvider.ReadAllTextAsync("/LoginPage.html"), false, false, true));
|
|
|
|
}
|
|
public async Task SignupAsync(ServerContext ctx)
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await AssetProvider.ReadAllTextAsync("/SignupPage.html"), false, false, true));
|
|
|
|
}
|
|
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);
|
|
var account = provider.GetUserAccount(name);
|
|
if (account.IsVerified)
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
|
|
else
|
|
{
|
|
var code = GetOrGenerateVerification(account);
|
|
await Creator.SendVerificationEmailAsync(account, code);
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageVerifyEmail.RenderAsync(new { Email = email })));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 (provider.ContainsSession(cookie));
|
|
|
|
provider.CreateSession(cookie, a.Id);
|
|
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 void Logout(ServerContext ctx)
|
|
{
|
|
if (ctx.RequestHeaders.TryGetValue("Cookie", out var cookies))
|
|
{
|
|
foreach (var c in cookies)
|
|
{
|
|
foreach(var cE in c.Split(new string[]{"; "},StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
|
|
var co = cE.Split(new char[] { '=' }, 2);
|
|
if (co.Length == 2 && co[0] == "Session")
|
|
{
|
|
if (provider.ContainsSession(co[1]))
|
|
{
|
|
provider.DeleteSession(co[1]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ctx.RequestHeaders.TryGetFirst("Authorization", out var auth))
|
|
{
|
|
var co = auth.Split(new char[] { ' ' }, 2);
|
|
if (co.Length == 2 && co[0] == "Bearer")
|
|
{
|
|
if (provider.ContainsSession(co[1]))
|
|
{
|
|
provider.DeleteSession(co[1]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
HttpClient client = new HttpClient();
|
|
private async Task SendEvent(EventType type, string userpropername, string username, string name, string propername, string description, string body)
|
|
{
|
|
CMSEvent evt = new CMSEvent() { Type = type, Username = username, Name = name, ProperName = propername, UserProperName = userpropername, Description = description, Body = body };
|
|
try
|
|
{
|
|
SendEvents?.Invoke(evt);
|
|
}
|
|
catch (Exception ex) { _ = ex; }
|
|
foreach (var user in provider.GetUsers())
|
|
{
|
|
foreach (var wh in user.Webhooks)
|
|
{
|
|
if (wh.Username != username) continue;
|
|
if ((type == EventType.MovieCreate || type == EventType.MovieUpdate) && wh.EnabledMovies)
|
|
{
|
|
switch (wh.Type)
|
|
{
|
|
case WebHookType.Ntfy:
|
|
//ntfy is the easiest
|
|
{
|
|
|
|
string con = $"Movie {propername} by {userpropername} was {(type == EventType.MovieCreate ? "created" : "updated")}";
|
|
|
|
try
|
|
{
|
|
await SendNtfyAsync($"{Configuration.Root.TrimEnd('/')}/user/{username}/movie/{name}/", wh.Url, wh.Key, wh.Priority, con, body);
|
|
//(await client.PostAsync(wh.Url,new StringContent(con))).Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_ = ex;
|
|
}
|
|
}
|
|
break;
|
|
case WebHookType.Gotify:
|
|
{
|
|
string con = $"Movie {propername} by {userpropername} was {(type == EventType.MovieCreate ? "created" : "updated")}";
|
|
|
|
await SendGotifyAsync(wh.Url, wh.Key, wh.Priority, con, body);
|
|
}
|
|
break;
|
|
case WebHookType.Other:
|
|
(await client.PostAsync(wh.Url, new StringContent(JsonConvert.SerializeObject(new { key = wh.Key, data = evt }), Encoding.UTF8, "application/json"))).Dispose();
|
|
break;
|
|
}
|
|
}
|
|
if ((type == EventType.ShowCreate || type == EventType.ShowUpdate) && wh.EnabledShows)
|
|
{
|
|
switch (wh.Type)
|
|
{
|
|
case WebHookType.Ntfy:
|
|
//ntfy is the easiest
|
|
{
|
|
|
|
string con = $"Show {propername} by {userpropername} was {(type == EventType.ShowCreate ? "created" : "updated")}";
|
|
|
|
try
|
|
{
|
|
await SendNtfyAsync($"{Configuration.Root.TrimEnd('/')}/user/{username}/show/{name}/", wh.Url, wh.Key, wh.Priority, con, body);
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_ = ex;
|
|
}
|
|
}
|
|
break;
|
|
case WebHookType.Gotify:
|
|
{
|
|
string con = $"Show {propername} by {userpropername} was {(type == EventType.ShowCreate ? "created" : "updated")}";
|
|
|
|
await SendGotifyAsync(wh.Url, wh.Key, wh.Priority, con, body);
|
|
}
|
|
break;
|
|
case WebHookType.Other:
|
|
(await client.PostAsync(wh.Url, new StringContent(JsonConvert.SerializeObject(new { key = wh.Key, data = evt }), Encoding.UTF8, "application/json"))).Dispose();
|
|
break;
|
|
}
|
|
}
|
|
if ((type == EventType.AlbumCreate || type == EventType.AlbumUpdate) && wh.EnabledShows)
|
|
{
|
|
switch (wh.Type)
|
|
{
|
|
case WebHookType.Ntfy:
|
|
//ntfy is the easiest
|
|
{
|
|
|
|
string con = $"Album {propername} by {userpropername} was {(type == EventType.AlbumCreate ? "created" : "updated")}";
|
|
|
|
try
|
|
{
|
|
await SendNtfyAsync($"{Configuration.Root.TrimEnd('/')}/user/{username}/album/{name}/", wh.Url, wh.Key, wh.Priority, con, body);
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_ = ex;
|
|
}
|
|
}
|
|
break;
|
|
case WebHookType.Gotify:
|
|
{
|
|
string con = $"Album {propername} by {userpropername} was {(type == EventType.AlbumCreate ? "created" : "updated")}";
|
|
|
|
await SendGotifyAsync(wh.Url, wh.Key, wh.Priority, con, body);
|
|
}
|
|
break;
|
|
case WebHookType.Other:
|
|
(await client.PostAsync(wh.Url, new StringContent(JsonConvert.SerializeObject(new { key = wh.Key, data = evt }), Encoding.UTF8, "application/json"))).Dispose();
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task SendNtfyAsync(string cmsurl, string url, string key, int priority, string con, string body)
|
|
{
|
|
try
|
|
{
|
|
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url))
|
|
{
|
|
request.Headers.Add("Title", con);
|
|
request.Headers.Add("Click", cmsurl);
|
|
request.Headers.Add("Priority", priority.ToString());
|
|
if (!string.IsNullOrWhiteSpace(key))
|
|
{
|
|
request.Headers.Add("Authorization", $"Bearer {key}");
|
|
}
|
|
request.Content = new StringContent(body);
|
|
(await client.SendAsync(request)).Dispose();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_ = ex;
|
|
}
|
|
}
|
|
|
|
private async Task SendGotifyAsync(string url, string key, int prio, string con, string body)
|
|
{
|
|
try
|
|
{
|
|
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"{url.TrimEnd('/')}/message");
|
|
request.Headers.Add("X-Gotify-Key", key);
|
|
request.Content = new StringContent(JsonConvert.SerializeObject(new { message = body, title = con, priority = prio }), Encoding.UTF8, "application/json");
|
|
(await client.SendAsync(request)).Dispose();
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_ = ex;
|
|
}
|
|
}
|
|
private string CreateCSRF(long account, string cookie)
|
|
{
|
|
lock (CsrfTokens)
|
|
{
|
|
CSRF csrf = new CSRF(account, cookie);
|
|
CsrfTokens.Add(csrf);
|
|
return csrf.CSRFToken;
|
|
}
|
|
}
|
|
|
|
|
|
List<CSRF> CsrfTokens { get; set; } = new List<CSRF>();
|
|
|
|
private UserAccount GetAccount(ServerContext ctx, bool requiresCSRF = false)
|
|
{
|
|
|
|
return GetAccount(ctx, out var s, requiresCSRF);
|
|
}
|
|
bool IsValidCSRFAndDestroy(long account, string cookie, string csrf)
|
|
{
|
|
lock (CsrfTokens)
|
|
{
|
|
|
|
var now = DateTime.Now;
|
|
foreach (var token in CsrfTokens)
|
|
{
|
|
if (token.Expires > now && cookie == token.Cookie && csrf == token.CSRFToken && account == token.UserId)
|
|
{
|
|
CsrfTokens.Remove(token);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
private UserAccount GetAccount(ServerContext ctx, out string cookie, bool requiresCSRF = false)
|
|
{
|
|
cookie = "";
|
|
if (ctx.RequestHeaders.TryGetValue("Cookie", out var cookies))
|
|
{
|
|
|
|
foreach (var c in cookies)
|
|
{
|
|
foreach(var cE in c.Split(new string[]{"; "},StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
var co = cE.Split(new char[] { '=' }, 2);
|
|
if (co.Length == 2 && co[0] == "Session")
|
|
{
|
|
cookie = co[1];
|
|
long? account = provider.GetSession(cookie);
|
|
|
|
|
|
|
|
if (account.HasValue)
|
|
{
|
|
if (requiresCSRF)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("csrf", out var csrf))
|
|
{
|
|
if (IsValidCSRFAndDestroy(account.Value, cookie, csrf))
|
|
{
|
|
return provider.GetUserById(account.Value);
|
|
}
|
|
}
|
|
throw new InvalidCSRFException();
|
|
}
|
|
|
|
return provider.GetUserById(account.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ctx.RequestHeaders.TryGetFirst("Authorization", out var auth))
|
|
{
|
|
var co = auth.Split(new char[] { ' ' }, 2);
|
|
if (co.Length == 2 && co[0] == "Bearer")
|
|
{
|
|
long? account = provider.GetSession(co[1]);
|
|
if (account.HasValue)
|
|
return provider.GetUserById(account.Value);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
private async Task UsersAsync(ServerContext ctx)
|
|
{
|
|
List<object> users = new List<object>();
|
|
foreach (var user in provider.GetUsers())
|
|
{
|
|
if (!user.IsAdmin && Configuration.Publish == CMSPublish.Admin)
|
|
{
|
|
//await ctx.SendTextAsync(await RenderHtmlAsync(ctx,"<h1>You can't upload content</h1>"));
|
|
continue;
|
|
}
|
|
if (!(user.IsAdmin || user.IsInvited) && Configuration.Publish == CMSPublish.RequireInvite)
|
|
{
|
|
//await ctx.SendTextAsync(await RenderHtmlAsync(ctx,"<h1>You can't upload content</h1>"));
|
|
continue;
|
|
}
|
|
if (!(user.IsAdmin || user.IsInvited || user.IsVerified))
|
|
{
|
|
//await ctx.SendTextAsync(await RenderHtmlAsync(ctx,"<h1>You can't upload content</h1>"));
|
|
continue;
|
|
}
|
|
|
|
users.Add(user.Scriban());
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageUsers.RenderAsync(new
|
|
{
|
|
Users = users,
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/"
|
|
})));
|
|
}
|
|
const string badC = "/\\\"&,?|:;*@!# ";
|
|
const string badCKeepSpace = "/\\\"&,?|:;*@!#";
|
|
private string FixStringKeepSpace(string str)
|
|
{
|
|
StringBuilder b = new StringBuilder();
|
|
foreach (var item in str)
|
|
{
|
|
if (char.IsControl(item))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (item >= 127) continue;
|
|
if (badCKeepSpace.Contains(item.ToString())) continue;
|
|
b.Append(item.ToString());
|
|
}
|
|
return b.ToString();
|
|
|
|
}
|
|
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, true);
|
|
if (account != null)
|
|
{
|
|
if (!account.IsAdmin && Configuration.Publish == CMSPublish.Admin)
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>You can't upload content</h1>"));
|
|
return;
|
|
}
|
|
if (!(account.IsAdmin || account.IsInvited) && Configuration.Publish == CMSPublish.RequireInvite)
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>You can't upload content</h1>"));
|
|
return;
|
|
}
|
|
if (!(account.IsAdmin || account.IsInvited || account.IsVerified))
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>You can't upload content</h1>"));
|
|
return;
|
|
}
|
|
name = FixString(name);
|
|
switch (type)
|
|
{
|
|
case "movie":
|
|
var movie = provider.CreateMovie(account.Username, name, proper_name, description.Replace("\r", ""));
|
|
ScheduleTask(async () =>
|
|
{
|
|
await this.Creator.EmailMovieAsync(provider, account, movie, false);
|
|
|
|
await SendEvent(EventType.MovieCreate, account.ProperName, account.Username, movie.Name, movie.ProperName, movie.Description, "");
|
|
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{account.Username}/movie/{name}/edit");
|
|
break;
|
|
case "show":
|
|
var show = provider.CreateShow(account.Username, name, proper_name, description);
|
|
ScheduleTask(async () =>
|
|
{
|
|
await this.Creator.EmailShowAsync(provider, account, show, false);
|
|
await SendEvent(EventType.ShowCreate, account.ProperName, account.Username, show.Name, show.ProperName, show.Description, "");
|
|
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{account.Username}/show/{name}/edit");
|
|
break;
|
|
case "album":
|
|
var album = provider.CreateAlbum(account.Username, name, proper_name, description);
|
|
ScheduleTask(async () =>
|
|
{
|
|
await this.Creator.EmailAlbumAsync(provider, account, album, false);
|
|
await SendEvent(EventType.AlbumCreate, account.ProperName, account.Username, album.Name, album.ProperName, album.Description, "");
|
|
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{account.Username}/album/{name}/edit");
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/");
|
|
}
|
|
}
|
|
|
|
public async Task UploadPage1(ServerContext ctx)
|
|
{
|
|
var account = GetAccount(ctx, out var cookie);
|
|
if (account != null)
|
|
{
|
|
var csrf = HttpUtility.HtmlAttributeEncode(CreateCSRF(account.Id, cookie));
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await RenderUpload1Async(csrf), false, false, false, true));
|
|
}
|
|
}
|
|
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(user, item.Name);
|
|
movies.Add(item.Scriban(data.ThumbnailUrl));
|
|
}
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageMovies.RenderAsync(new { Movies = movies })));
|
|
}
|
|
private void RegisterUsersPath()
|
|
{
|
|
RouteServer routeServer = new RouteServer();
|
|
routeServer.Add("/", UserPageAsync);
|
|
routeServer.Add("/about", AboutAsync);
|
|
routeServer.Add("/mailinglist", MailingListAsync);
|
|
routeServer.Add("/mailinglist", MailingListPostAsync, "POST");
|
|
routeServer.Add("/webhooks", Webhooks);
|
|
routeServer.Add("/update_webhook", UpdateWebhook, "POST");
|
|
routeServer.Add("/create_webhook", CreateWebhook, "POST");
|
|
usersMountableServer.Mount("/", routeServer);
|
|
usersMountableServer.Mount("/movies/", Movies());
|
|
usersMountableServer.Mount("/movie/", moviePathValueServer);
|
|
usersMountableServer.Mount("/shows/", Shows());
|
|
usersMountableServer.Mount("/show/", showPathValueServer);
|
|
usersMountableServer.Mount("/albums/", Albums());
|
|
usersMountableServer.Mount("/album/", albumPathValueServer);
|
|
RegisterMoviePath();
|
|
RegisterShowPath();
|
|
RegisterAlbumPath();
|
|
}
|
|
|
|
private async Task CreateWebhook(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string username = usersPathValueServer.GetValue(ctx);
|
|
var theAccount = provider.GetUserAccount(username);
|
|
var account = GetAccount(ctx,true);
|
|
if (account != null)
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("key", out var key)) key = "";
|
|
if (!ctx.QueryParams.TryGetFirstInt32("priority", out var priority)) priority = 2;
|
|
bool enablemovies = ctx.QueryParams.GetFirstBoolean("enablemovies");
|
|
bool enableshows = ctx.QueryParams.GetFirstBoolean("enableshows");
|
|
bool enablealbums = ctx.QueryParams.GetFirstBoolean("enablealbums");
|
|
bool enablesoftware = ctx.QueryParams.GetFirstBoolean("enablesoftware");
|
|
bool enableother = ctx.QueryParams.GetFirstBoolean("enableother");
|
|
if (ctx.QueryParams.TryGetFirst("name", out var name) && ctx.QueryParams.TryGetFirst("url", out var url) && ctx.QueryParams.TryGetFirst("type", out var type))
|
|
{
|
|
WebHook hook = new WebHook
|
|
{
|
|
Username = username,
|
|
Priority = priority,
|
|
Url = url,
|
|
WebhookName = name,
|
|
Type = type == "ntfy" ? WebHookType.Ntfy : (type == "gotify" ? WebHookType.Gotify : WebHookType.Other),
|
|
Key = key,
|
|
EnabledMovies = enablemovies,
|
|
EnabledShows = enableshows,
|
|
EnabledAlbums = enablealbums,
|
|
EnabledSoftware = enablesoftware,
|
|
EnabledOther = enableother
|
|
};
|
|
account.Webhooks.Add(hook);
|
|
provider.UpdateUser(account);
|
|
}
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{username}/webhooks");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
|
|
}
|
|
}
|
|
|
|
private async Task UpdateWebhook(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string username = usersPathValueServer.GetValue(ctx);
|
|
var theAccount = provider.GetUserAccount(username);
|
|
var account = GetAccount(ctx,true);
|
|
if (account != null)
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("key", out var key)) key = "";
|
|
if (!ctx.QueryParams.TryGetFirstInt32("priority", out var priority)) priority = 2;
|
|
|
|
bool enablemovies = ctx.QueryParams.GetFirstBoolean("enablemovies");
|
|
bool enableshows = ctx.QueryParams.GetFirstBoolean("enableshows");
|
|
bool enablealbums = ctx.QueryParams.GetFirstBoolean("enablealbums");
|
|
bool enablesoftware = ctx.QueryParams.GetFirstBoolean("enablesoftware");
|
|
bool enableother = ctx.QueryParams.GetFirstBoolean("enableother");
|
|
if (ctx.QueryParams.TryGetFirst("name", out var name) && ctx.QueryParams.TryGetFirst("url", out var url))
|
|
{
|
|
foreach (var item in account.Webhooks)
|
|
{
|
|
if (item.WebhookName == name && item.Username == username)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("action", out var val) && val == "Delete")
|
|
{
|
|
account.Webhooks.Remove(item);
|
|
provider.UpdateUser(account);
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{username}/webhooks");
|
|
return;
|
|
}
|
|
item.EnabledMovies = enablemovies;
|
|
item.EnabledShows = enableshows;
|
|
item.EnabledAlbums = enablealbums;
|
|
item.EnabledSoftware = enablesoftware;
|
|
item.EnabledOther = enableother;
|
|
item.Key = key;
|
|
item.Priority = priority;
|
|
item.Url = url;
|
|
|
|
provider.UpdateUser(account);
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{username}/webhooks");
|
|
return;
|
|
}
|
|
}
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{username}/webhooks");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task Webhooks(ServerContext ctx)
|
|
{
|
|
string username = usersPathValueServer.GetValue(ctx);
|
|
var theAccount = provider.GetUserAccount(username);
|
|
var account = GetAccount(ctx);
|
|
if (account != null)
|
|
{
|
|
List<object> webhooks = new List<object>();
|
|
foreach (var item in account.Webhooks)
|
|
{
|
|
if (item.Username != username) continue;
|
|
|
|
webhooks.Add(new { name = item.WebhookName, ntfy = item.Type == WebHookType.Ntfy, gotify = item.Type == WebHookType.Gotify, priority = item.Priority, url = item.Url, key = item.Key, enablemovies = item.EnabledMovies, enableshows = item.EnabledShows, enablealbums = item.EnabledAlbums, enablesoftware = item.EnabledSoftware, enableother = item.EnabledOther });
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageWebhook.RenderAsync(new
|
|
{
|
|
webhooks
|
|
})));
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
|
|
}
|
|
}
|
|
|
|
private async Task MailingListPostAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
var theAccount = provider.GetUserAccount(user);
|
|
var account = GetAccount(ctx,true);
|
|
if (account != null)
|
|
{
|
|
ctx.ParseBody();
|
|
MailEntry entry = null;
|
|
foreach (var item in theAccount.AccountsToMail)
|
|
{
|
|
if (item.UserId == account.Id)
|
|
{
|
|
entry = item;
|
|
break;
|
|
}
|
|
}
|
|
if (entry == null)
|
|
{
|
|
entry = new MailEntry() { UserId = account.Id };
|
|
theAccount.AccountsToMail.Add(entry);
|
|
}
|
|
entry.EnableMovies = ctx.QueryParams.ContainsKey("enablemovies");
|
|
entry.EnableShows = ctx.QueryParams.ContainsKey("enableshows");
|
|
entry.EnableSingles = ctx.QueryParams.ContainsKey("enablesingles");
|
|
entry.EnableAlbums = ctx.QueryParams.ContainsKey("enablealbums");
|
|
entry.EnableSoftware = ctx.QueryParams.ContainsKey("enablesoftware");
|
|
entry.EnableOther = ctx.QueryParams.ContainsKey("enableother");
|
|
entry.EnableUpdates = ctx.QueryParams.ContainsKey("enableupdates");
|
|
|
|
provider.UpdateUser(theAccount);
|
|
}
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/");
|
|
}
|
|
private async Task MailingListAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
var theAccount = provider.GetUserAccount(user);
|
|
var account = GetAccount(ctx);
|
|
if (account != null)
|
|
{
|
|
MailEntry entry = new MailEntry() { UserId = account.Id };
|
|
foreach (var item in theAccount.AccountsToMail)
|
|
{
|
|
if (item.UserId == account.Id)
|
|
{
|
|
entry = item;
|
|
break;
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageMailingList.RenderAsync(new
|
|
{
|
|
enablemovies = entry.EnableMovies,
|
|
enableshows = entry.EnableShows,
|
|
enablesingles = entry.EnableSingles,
|
|
enablealbums = entry.EnableAlbums,
|
|
enablesoftware = entry.EnableSoftware,
|
|
enableother = entry.EnableOther,
|
|
enableupdates = entry.EnableUpdates
|
|
})));
|
|
return;
|
|
}
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/login");
|
|
}
|
|
private async Task EditAlbumPagePostAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
var _album = provider.GetAlbum(user, album);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
if (_album != null)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("proper_name", out var proper_name) && ctx.QueryParams.TryGetFirst("description", out var description) && ctx.QueryParams.TryGetFirst("album_artist", out var album_artst) && ctx.QueryParams.TryGetFirst("year", out var yearS) && int.TryParse(yearS, out var year))
|
|
{
|
|
_album.ProperName = proper_name;
|
|
_album.Description = description.Replace("\r", "");
|
|
_album.AlbumArtist = FixStringKeepSpace(album_artst);
|
|
_album.Year = year;
|
|
provider.UpdateAlbum(_album);
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
private void RegisterAlbumPath()
|
|
{
|
|
albumRouteServer.Add("/", AlbumPageAsync);
|
|
albumRouteServer.Add("/play", PlayAlbumAsync);
|
|
albumRouteServer.Add("/edit", EditAlbumPageAsync);
|
|
albumRouteServer.Add("/edit", EditAlbumPagePostAsync, "POST");
|
|
albumRouteServer.Add("/upload", UploadAlbumStreamAsync, "POST");
|
|
albumRouteServer.Add("/sendupdate", SendAlbumUpdateAsync, "POST");
|
|
albumRouteServer.Add("/edit_tracklist", EditTracklistAsync);
|
|
albumRouteServer.Add("/edit_tracklist", EditTracklistPostAsync, "POST");
|
|
albumRouteServer.Add("/upload_extra", UploadAlbumExtraAsync, "POST");
|
|
albumRouteServer.Add("/extras", ExtrasAlbumPageAsync);
|
|
albumRouteServer.Add("/mkdir", ExtrasAlbumMkdirAsync, "POST");
|
|
|
|
}
|
|
private async Task EditTracklistPostAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
|
|
var _album = provider.GetAlbum(user, album);
|
|
var _user = provider.GetUserAccount(user);
|
|
|
|
var me = GetAccount(ctx,true);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (_album != null && me != null && _user != null)
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(path, user, "album", album));
|
|
var tmpFile = Path.Combine(path, user, "album", album, $"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
|
|
foreach (var item in ctx.ParseBody((n, fn, ct) => File.Create(tmpFile)))
|
|
item.Value.Dispose();
|
|
|
|
if (!ctx.QueryParams.TryGetFirst("operation", out var operation))
|
|
operation = "";
|
|
|
|
|
|
if (!ctx.QueryParams.TryGetFirstInt32("track_id", out var track_id))
|
|
track_id = 0;
|
|
|
|
switch (operation)
|
|
{
|
|
case "Yes":
|
|
{
|
|
string oldtrack = _album.Tracks[track_id];
|
|
string flac = Path.Combine(path, user, "album", album, $"{(track_id + 1).ToString("D2")} {_album.AlbumArtist} - {oldtrack}.flac");
|
|
if (File.Exists(flac)) File.Delete(flac);
|
|
|
|
string oldmp3 = Path.Combine(path, user, "album", album, $"{oldtrack}.mp3");
|
|
if (File.Exists(oldmp3)) File.Delete(oldmp3);
|
|
|
|
|
|
|
|
_album.Tracks.RemoveAt(track_id);
|
|
for (int i = track_id; i < _album.Tracks.Count; i++)
|
|
{
|
|
string oldflac = Path.Combine(path, user, "album", album, $"{(track_id + 2).ToString("D2")} {_album.AlbumArtist} - {oldtrack}.flac");
|
|
string newflac = Path.Combine(path, user, "album", album, $"{(track_id + 1).ToString("D2")} {_album.AlbumArtist} - {oldtrack}.flac");
|
|
|
|
if (File.Exists(oldflac)) File.Move(oldflac, newflac);
|
|
}
|
|
provider.UpdateAlbum(_album);
|
|
}
|
|
break;
|
|
case "Delete":
|
|
{
|
|
string oldtrack = _album.Tracks[track_id];
|
|
string text = $"<h1>Delete Track: {oldtrack}?</h1><span>Note the media files will be removed</span><form method=\"POST\" action=\"./edit_tracklist\"><input type=\"hidden\" name=\"track_id\" value=\"{track_id}\"><input type=\"submit\" name=\"operation\" value=\"Yes\" class=\"btn btn-danger\" value=\"Yes\"><a href=\"./edit_tracklist\" class=\"btn btn-primary\">No</a></form>";
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, text));
|
|
return;
|
|
}
|
|
|
|
case "Upload":
|
|
{
|
|
string oldtrack = _album.Tracks[track_id];
|
|
string flac = Path.Combine(path, user, "album", album, $"{(track_id + 1).ToString("D2")} {_album.AlbumArtist} - {oldtrack}.flac");
|
|
|
|
File.Move(tmpFile, flac);
|
|
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileAlbumAsync(user, album);
|
|
});
|
|
|
|
string oldmp3 = Path.Combine(path, user, "album", album, $"{oldtrack}.mp3");
|
|
|
|
ScheduleFFmpeg($"-y -i \"{flac}\" {Configuration.BrowserTranscodeMp3} \"{oldmp3}\"");
|
|
}
|
|
break;
|
|
case "Rename":
|
|
{
|
|
if (track_id < _album.Tracks.Count)
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("track_name", out var track_name))
|
|
track_name = $"Track {track_id.ToString("D2")}";
|
|
|
|
track_name = FixStringKeepSpace(track_name);
|
|
|
|
string oldtrack = _album.Tracks[track_id];
|
|
string oldflac = Path.Combine(path, user, "album", album, $"{(track_id + 1).ToString("D2")} {_album.AlbumArtist} - {oldtrack}.flac");
|
|
string oldmp3 = Path.Combine(path, user, "album", album, $"{oldtrack}.mp3");
|
|
string newflac = Path.Combine(path, user, "album", album, $"{(track_id + 1).ToString("D2")} {_album.AlbumArtist} - {track_name}.flac");
|
|
string newmp3 = Path.Combine(path, user, "album", album, $"{track_name}.mp3");
|
|
|
|
if (oldflac != newflac && File.Exists(oldflac))
|
|
File.Move(oldflac, newflac);
|
|
if (oldmp3 != newmp3 && File.Exists(oldmp3))
|
|
File.Move(oldmp3, newmp3);
|
|
|
|
_album.Tracks[track_id] = track_name;
|
|
provider.UpdateAlbum(_album);
|
|
}
|
|
}
|
|
break;
|
|
case "Add":
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("track_name", out var track_name))
|
|
track_name = $"Track {track_id.ToString("D2")}";
|
|
|
|
_album.Tracks.Add(FixStringKeepSpace(track_name));
|
|
provider.UpdateAlbum(_album);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (File.Exists(tmpFile))
|
|
File.Delete(tmpFile);
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/album/{album}/edit_tracklist");
|
|
}
|
|
}
|
|
private async Task EditTracklistAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
|
|
var _album = provider.GetAlbum(user, album);
|
|
var _user = provider.GetUserAccount(user);
|
|
|
|
var me = GetAccount(ctx,out var cookie);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (_album != null && me != null && _user != null)
|
|
{
|
|
List<object> tracks = new List<object>();
|
|
string csrf="";
|
|
if(me != null)
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
|
|
for (int i = 0; i < _album.Tracks.Count; i++)
|
|
{
|
|
tracks.Add(new { index = i, name = HttpUtility.HtmlAttributeEncode(_album.Tracks[i]) });
|
|
}
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageTracklist.RenderAsync(new { tracks,csrf })));
|
|
}
|
|
|
|
}
|
|
|
|
private async Task PlayAlbumAsync(ServerContext ctx)
|
|
{
|
|
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
|
|
var _album = provider.GetAlbum(user, album);
|
|
var _user = provider.GetUserAccount(user);
|
|
|
|
var me = GetAccount(ctx);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (_album != null && _user != null)
|
|
{
|
|
List<object> list = new List<object>();
|
|
int i = 1;
|
|
foreach (var item in _album.Tracks)
|
|
{
|
|
list.Add(new
|
|
{
|
|
artist = _album.AlbumArtist,
|
|
album = _album.ProperName,
|
|
name = item,
|
|
art = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/thumbnail.jpg",
|
|
url = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/{HttpUtility.UrlPathEncode(item)}.mp3",
|
|
download = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/{i.ToString("D2")}%20{HttpUtility.UrlPathEncode(_album.AlbumArtist)}%20-%20{HttpUtility.UrlPathEncode(item)}.flac"
|
|
});
|
|
i++;
|
|
}
|
|
|
|
object v = new
|
|
{
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
list = JsonConvert.SerializeObject(list)
|
|
};
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageMusicPlayer.RenderAsync(v)));
|
|
}
|
|
}
|
|
private async Task AlbumPageAsync(ServerContext ctx)
|
|
{
|
|
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
|
|
var _album = provider.GetAlbum(user, album);
|
|
var _user = provider.GetUserAccount(user);
|
|
|
|
var me = GetAccount(ctx,out var cookie);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
object value;
|
|
if (_album != null && _user != null)
|
|
{
|
|
string albumDir = Path.Combine(this.path, user, "album", album);
|
|
bool torrent = File.Exists(Path.Combine(albumDir, $"{album}.torrent"));
|
|
bool torrent_wextra = File.Exists(Path.Combine(albumDir, $"{album}_withextras.torrent"));
|
|
bool extrasexists = Directory.Exists(Path.Combine(albumDir, "extras")) || me != null;
|
|
|
|
string thumb = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/thumbnail.jpg";
|
|
string csrf="";
|
|
if(me != null)
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
value = new
|
|
{
|
|
csrf,
|
|
extrasexists,
|
|
torrentexists = torrent,
|
|
torrentwextraexists = torrent_wextra,
|
|
torrent = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/{album}.torrent",
|
|
torrentwextra = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{album}/{album}_withextras.torrent",
|
|
editable = me != null,
|
|
userproper = HttpUtility.HtmlEncode(_user.ProperName),
|
|
username = HttpUtility.HtmlEncode(user),
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasalbum = true,
|
|
albumthumbnail = thumb,
|
|
albumproper = HttpUtility.HtmlEncode(_album.ProperName),
|
|
albumname = HttpUtility.HtmlEncode(_album.Name),
|
|
albumdescription = DescriptLinkUtils(_album.Description ?? "").Replace("\n", "<br>")
|
|
};
|
|
}
|
|
else
|
|
{
|
|
value = new
|
|
{
|
|
username = user,
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasalbum = false
|
|
};
|
|
}
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageAlbum.RenderAsync(value)));
|
|
|
|
|
|
|
|
}
|
|
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");
|
|
showRouteServer.Add("/upload_extra", UploadShowExtraAsync, "POST");
|
|
showRouteServer.Add("/extras", ExtrasShowPageAsync);
|
|
showRouteServer.Add("/mkdir", ExtrasShowMkdirAsync, "POST");
|
|
showRouteServer.Add("/sendupdate", SendShowUpdateAsync, "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");
|
|
|
|
seasonMountableServer.Mount("/episode/", episodePathValueServer);
|
|
RegisterEpisodePath();
|
|
}
|
|
|
|
private void RegisterEpisodePath()
|
|
{
|
|
episodeRouteServer.Add("/", EpisodePageAsync);
|
|
episodeRouteServer.Add("/edit", EditEpisodePageAsync);
|
|
episodeRouteServer.Add("/edit", EditEpisodePagePostAsync, "POST");
|
|
episodeRouteServer.Add("/upload", UploadEpisodeStreamAsync, "POST");
|
|
episodeRouteServer.Add("/subtitles", SubtitlesEpisodeAsync);
|
|
episodeRouteServer.Add("/subtitles", SubtitlesEpisodePostAsync, "POST");
|
|
episodeRouteServer.Add("/play", PlayEpisodePageAsync);
|
|
}
|
|
|
|
private async Task EpisodePageAsync(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);
|
|
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
string episodeS = episodePathValueServer.GetValue(ctx);
|
|
|
|
if (!int.TryParse(seasonS, out var season)) season = 1;
|
|
if (!int.TryParse(episodeS, out var episode)) episode = 1;
|
|
|
|
var _season = provider.GetSeason(user, show, season);
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
object value;
|
|
if (_show != null && _user != null)
|
|
{
|
|
string episodeDir = Path.Combine(this.path, user, "show", show, $"Season {season.ToString("D2")}");
|
|
bool episodebrowserexists = File.Exists(Path.Combine(episodeDir, $"S{season.ToString("D2")}E{episode.ToString("D2")}.mp4"));
|
|
string name = $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}";
|
|
bool episodeexists = File.Exists(Path.Combine(episodeDir, $"{name}.mp4"));
|
|
string thumb = File.Exists(Path.Combine(episodeDir, $"{name}-thumbnail.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/{_episode.EpisodeName}%20S{season.ToString("D2")}E{episode.ToString("D2")}-thumbnail.jpg" : File.Exists(Path.Combine(episodeDir, "thumbnail.jpg")) ? $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/thumbnail.jpg" : $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg";
|
|
value = new
|
|
{
|
|
episodebrowserexists,
|
|
episodeexists,
|
|
downloadurl = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/{_episode.EpisodeName}%20S{season.ToString("D2")}E{episode.ToString("D2")}.mp4",
|
|
editable = me != null,
|
|
userproper = HttpUtility.HtmlEncode(_user.ProperName),
|
|
username = HttpUtility.HtmlEncode(user),
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasmovie = true,
|
|
episodethumbnail = thumb,
|
|
seasonproper = HttpUtility.HtmlEncode(_season.ProperName),
|
|
showpropername = HttpUtility.HtmlEncode(_show.ProperName),
|
|
episodeproperattr = HttpUtility.HtmlAttributeEncode(_episode.ProperName),
|
|
episodeproper = HttpUtility.HtmlEncode(_episode.ProperName),
|
|
episodename = HttpUtility.HtmlEncode(_episode.EpisodeName),
|
|
episodedescription = DescriptLinkUtils(_episode.Description ?? "").Replace("\n", "<br>")
|
|
};
|
|
}
|
|
else
|
|
{
|
|
value = new
|
|
{
|
|
username = user,
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasmovie = false
|
|
};
|
|
}
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageEpisode.RenderAsync(value)));
|
|
|
|
}
|
|
|
|
private async Task UploadEpisodeStreamAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
string episodeS = episodePathValueServer.GetValue(ctx);
|
|
if (!int.TryParse(seasonS, out var season))
|
|
season = 1;
|
|
if (!int.TryParse(episodeS, out var episode))
|
|
episode = 1;
|
|
|
|
|
|
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();
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
if (_episode != null)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("type", out var type))
|
|
{
|
|
switch (type)
|
|
{
|
|
case "thumbnail":
|
|
string thumb = Path.Combine(path, user, "show", show, $"Season {season.ToString("D2")}", $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}-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 {season.ToString("D2")}", $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}-poster.jpg");
|
|
if (File.Exists(poster)) File.Delete(poster);
|
|
File.Move(tmpFile, poster);
|
|
break;
|
|
case "movie":
|
|
string movie = Path.Combine(path, user, "show", show, $"Season {season.ToString("D2")}", $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}.mp4");
|
|
if (File.Exists(movie)) File.Delete(movie);
|
|
File.Move(tmpFile, movie);
|
|
ScheduleFFmpeg($"-y -i \"{movie}\" {Configuration.BrowserTranscode} \"{Path.Combine(path, user, "show", show, $"Season {season.ToString("D2")}", $"S{season.ToString("D2")}E{episode.ToString("D2")}.mp4")}\"");
|
|
break;
|
|
|
|
}
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileShowAsync(user, show);
|
|
});
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
|
|
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 async Task EditSeasonPagePostAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
if (!int.TryParse(seasonS, out var season))
|
|
season = 1;
|
|
|
|
var me = GetAccount(ctx,true);
|
|
var _season = provider.GetSeason(user, show, season);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
if (_season != null)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("proper_name", out var proper_name) && ctx.QueryParams.TryGetFirst("description", out var description))
|
|
{
|
|
_season.ProperName = proper_name;
|
|
_season.Description = description.Replace("\r", "");
|
|
provider.UpdateSeason(_season);
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
public async Task EditEpisodePageAsync(ServerContext ctx)
|
|
{
|
|
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,out var cookie);
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
string episodeS = episodePathValueServer.GetValue(ctx);
|
|
if (!int.TryParse(seasonS, out var season))
|
|
season = 1;
|
|
if (!int.TryParse(episodeS, out var episode))
|
|
episode = 1;
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
if (_episode != null)
|
|
{
|
|
string csrf="";
|
|
string csrf2="";
|
|
|
|
if(me != null)
|
|
{
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
csrf2 = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageEditEpisodeDetails.RenderAsync(new { Propername = System.Web.HttpUtility.HtmlAttributeEncode(_episode.ProperName), Description = System.Web.HttpUtility.HtmlEncode(_episode.Description), csrf, csrf2 })));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>You are unauthorized to edit this</h1>"));
|
|
}
|
|
|
|
}
|
|
|
|
private async Task EditEpisodePagePostAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
string episodeS = episodePathValueServer.GetValue(ctx);
|
|
if (!int.TryParse(seasonS, out var season))
|
|
season = 1;
|
|
if (!int.TryParse(episodeS, out var episode))
|
|
episode = 1;
|
|
|
|
var me = GetAccount(ctx,true);
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
if (_episode != null)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("proper_name", out var proper_name) && ctx.QueryParams.TryGetFirst("description", out var description))
|
|
{
|
|
_episode.ProperName = proper_name;
|
|
_episode.Description = description.Replace("\r", "");
|
|
provider.UpdateEpisode(_episode);
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
|
|
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,out var cookie);
|
|
|
|
var _season = provider.GetSeason(user, show, season);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
if (_season != null)
|
|
{
|
|
string csrf="";
|
|
string csrf2="";
|
|
string csrf3="";
|
|
if(me != null)
|
|
{
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
csrf2 = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
csrf3 = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, 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), csrf,csrf2,csrf3 })));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<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, $"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(ctx, 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 async Task EditShowPagePostAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
var _show = provider.GetShow(user, show);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
if (_show != null)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("proper_name", out var proper_name) && ctx.QueryParams.TryGetFirst("description", out var description))
|
|
{
|
|
_show.ProperName = proper_name;
|
|
_show.Description = description.Replace("\r", "");
|
|
provider.UpdateShow(_show);
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
|
|
private async Task EditShowPageAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,out var cookie);
|
|
var _show = provider.GetShow(user, show);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
if (_show != null)
|
|
{
|
|
string csrf="";
|
|
string csrf2="";
|
|
string csrf3="";
|
|
if(me != null)
|
|
{
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
csrf2 = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
csrf3 = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageEditShowDetails.RenderAsync(new { Propername = System.Web.HttpUtility.HtmlAttributeEncode(_show.ProperName), newseasonnumber = provider.SeasonCount(user, show) + 1, Description = System.Web.HttpUtility.HtmlEncode(_show.Description) ,csrf,csrf2,csrf3 })));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<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,out var cookie);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
object value;
|
|
if (_show != null && _user != null)
|
|
{
|
|
string csrf="";
|
|
if(me != null)
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
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
|
|
{
|
|
csrf,
|
|
seasons,
|
|
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(ctx, await pageShow.RenderAsync(value)));
|
|
|
|
}
|
|
|
|
private IServer Shows()
|
|
{
|
|
RouteServer routeServer = new RouteServer();
|
|
routeServer.Add("/", ShowsAsync);
|
|
return routeServer;
|
|
}
|
|
|
|
private IServer Albums()
|
|
{
|
|
RouteServer routeServer = new RouteServer();
|
|
routeServer.Add("/", AlbumsAsync);
|
|
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(ctx, await pageShows.RenderAsync(new { Shows = shows })));
|
|
}
|
|
|
|
|
|
private async Task AlbumsAsync(ServerContext ctx)
|
|
{
|
|
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
List<object> albums = new List<object>();
|
|
foreach (var item in provider.GetAlbums(user))
|
|
{
|
|
//var data = GetMovieContentMetaData(Configuration,user,item.Name);
|
|
string thumb = $"{Configuration.Root.TrimEnd('/')}/content/{user}/album/{item.Name}/thumbnail.jpg";
|
|
albums.Add(item.Scriban(thumb));
|
|
}
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageAlbums.RenderAsync(new { Albums = albums })));
|
|
}
|
|
|
|
private static 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;
|
|
}
|
|
internal static 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]);
|
|
}
|
|
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(ctx, 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(ctx, 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("/sendupdate", SendMovieUpdateAsync, "POST");
|
|
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 SendShowUpdateAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
var _user = provider.GetUserAccount(user);
|
|
var _show = provider.GetShow(user, show);
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
ctx.ParseBody();
|
|
if (!ctx.QueryParams.TryGetFirst("body", out var body))
|
|
{
|
|
body = "";
|
|
}
|
|
|
|
ScheduleTask(async () =>
|
|
{
|
|
await Creator.EmailShowAsync(provider, _user, _show, true, body);
|
|
await SendEvent(EventType.ShowUpdate, _user.ProperName, _user.Username, _show.Name, _show.ProperName, _show.Description, body);
|
|
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{show}/");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync("Failed to send update");
|
|
}
|
|
}
|
|
private async Task SendMovieUpdateAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string movie = moviePathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
var _user = provider.GetUserAccount(user);
|
|
var _movie = provider.GetMovie(user, movie);
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
ctx.ParseBody();
|
|
if (!ctx.QueryParams.TryGetFirst("body", out var body))
|
|
{
|
|
body = "";
|
|
}
|
|
ScheduleTask(async () =>
|
|
{
|
|
await Creator.EmailMovieAsync(provider, _user, _movie, true, body);
|
|
await SendEvent(EventType.MovieUpdate, _user.ProperName, _user.Username, _movie.Name, _movie.ProperName, _movie.Description, body);
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/movie/{movie}/");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync("Failed to send update");
|
|
}
|
|
}
|
|
private async Task SendAlbumUpdateAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
var _user = provider.GetUserAccount(user);
|
|
var _album = provider.GetAlbum(user, album);
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
ctx.ParseBody();
|
|
if (!ctx.QueryParams.TryGetFirst("body", out var body))
|
|
{
|
|
body = "";
|
|
}
|
|
ScheduleTask(async () =>
|
|
{
|
|
await Creator.EmailAlbumAsync(provider, _user, _album, true, body);
|
|
await SendEvent(EventType.AlbumUpdate, _user.ProperName, _user.Username, _album.Name, _album.ProperName, _album.Description, body);
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/album/{album}/");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync("Failed to send update");
|
|
}
|
|
}
|
|
|
|
private async Task SubtitlesMoviePostAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string movie = moviePathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,out var cookie,true);
|
|
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);
|
|
}
|
|
var csrf=HttpUtility.UrlEncode(this.CreateCSRF(me.Id, cookie));
|
|
await ctx.SendJsonAsync(new{success=true,csrf});
|
|
return;
|
|
}
|
|
}
|
|
ctx.StatusCode = 400;
|
|
await ctx.SendJsonAsync(new{success=false});
|
|
}
|
|
private async Task SubtitlesEpisodeAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
string episodeS = episodePathValueServer.GetValue(ctx);
|
|
if (!int.TryParse(seasonS, out var season))
|
|
season = 1;
|
|
|
|
if (!int.TryParse(episodeS, out var episode))
|
|
episode = 1;
|
|
|
|
var me = GetAccount(ctx,out var cookie);
|
|
var _show = provider.GetMovie(user, show);
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
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, "show", show, $"Season {season.ToString("D2")}", $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}-subtitles", lang);
|
|
string langFile = Path.Combine(langDir, $"{_episode.ProperName} S{season.ToString("D2")}E{episode.ToString("D2")}.json");
|
|
string browserfile = $"{Configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{season.ToString("D2")}/S{season.ToString("D2")}E{episode.ToString("D2")}.mp4";
|
|
string csrf="";
|
|
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
|
|
string json = "";
|
|
bool hasjson = false;
|
|
if (File.Exists(langFile))
|
|
{
|
|
hasjson = true;
|
|
json = File.ReadAllText(langFile);
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageSubtitleEditor.RenderAsync(new { hasjson, json, lang, browserfile, csrf })));
|
|
}
|
|
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(ctx, await pageSubtitleLangList.RenderAsync(new { languages })));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private async Task SubtitlesEpisodePostAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
string episodeS = episodePathValueServer.GetValue(ctx);
|
|
if (!int.TryParse(seasonS, out var season))
|
|
season = 1;
|
|
|
|
if (!int.TryParse(episodeS, out var episode))
|
|
episode = 1;
|
|
|
|
var me = GetAccount(ctx,true);
|
|
var _show = provider.GetMovie(user, show);
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
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, "show", show, $"Season {season.ToString("D2")}", $"{_episode.EpisodeName} S{season.ToString("D2")}E{episode.ToString("D2")}-subtitles", lang);
|
|
Directory.CreateDirectory(langDir);
|
|
string langFile = Path.Combine(langDir, $"{_episode.ProperName} S{season.ToString("D2")}E{episode.ToString("D2")}.json");
|
|
string vtt = Path.Combine(langDir, $"{_episode.ProperName} S{season.ToString("D2")}E{episode.ToString("D2")}.vtt");
|
|
string srt = Path.Combine(langDir, $"{_episode.ProperName} S{season.ToString("D2")}E{episode.ToString("D2")}.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,out var cookie);
|
|
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);
|
|
}
|
|
var csrf=HttpUtility.UrlEncode(this.CreateCSRF(me.Id, cookie));
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageSubtitleEditor.RenderAsync(new { hasjson, json, lang, browserfile,csrf })));
|
|
}
|
|
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(ctx, await pageSubtitleLangList.RenderAsync(new { languages })));
|
|
}
|
|
|
|
}
|
|
}
|
|
private async Task UploadAlbumExtraAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
if (me != null)
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(path, user, "album", album));
|
|
var tmpFile = Path.Combine(path, user, "album", album, $"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, "album", album, "extras", _path);
|
|
if (File.Exists(_path2))
|
|
File.Delete(_path2);
|
|
File.Move(tmpFile, _path2);
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileAlbumAsync(user, album);
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/album/{album}/extras?path={System.Web.HttpUtility.UrlEncode(parent)}");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
private async Task UploadMovieExtraAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string movie = moviePathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
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 UploadShowExtraAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
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");
|
|
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, "show", show, "extras", _path);
|
|
if (File.Exists(_path2))
|
|
File.Delete(_path2);
|
|
File.Move(tmpFile, _path2);
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileShowAsync(user, show);
|
|
});
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{show}/extras?path={System.Web.HttpUtility.UrlEncode(parent)}");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private async Task ExtrasAlbumMkdirAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
|
|
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, "album", album, "extras", _path);
|
|
|
|
|
|
if (me != null)
|
|
{
|
|
Directory.CreateDirectory(_path2);
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/album/{album}/extras?path={System.Web.HttpUtility.UrlEncode(_path)}");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/album/{album}/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,true);
|
|
|
|
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)}");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/movie/{movie}/extras?path={System.Web.HttpUtility.UrlEncode(parent)}");
|
|
}
|
|
}
|
|
}
|
|
private async Task ExtrasShowMkdirAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
|
|
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, "show", show, "extras", _path);
|
|
|
|
|
|
if (me != null)
|
|
{
|
|
Directory.CreateDirectory(_path2);
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{show}/extras?path={System.Web.HttpUtility.UrlEncode(_path)}");
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendRedirectAsync($"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{show}/extras?path={System.Web.HttpUtility.UrlEncode(parent)}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task ExtrasShowPageAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
if (!ctx.QueryParams.TryGetFirst("path", out var __path))
|
|
{
|
|
__path = "/";
|
|
}
|
|
__path = __path.TrimStart('/');
|
|
|
|
__path = SanitizePath(__path);
|
|
string extrasPath = Path.Combine(path, user, "show", show, "extras");
|
|
|
|
if (__path.Length > 0)
|
|
{
|
|
extrasPath = Path.Combine(extrasPath, __path);
|
|
}
|
|
|
|
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" });
|
|
}
|
|
if (Directory.Exists(extrasPath))
|
|
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 });
|
|
}
|
|
if (Directory.Exists(extrasPath))
|
|
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}/show/{show}/extras/{System.Web.HttpUtility.UrlPathEncode(__path.TrimEnd('/') + '/' + filename)}";
|
|
paths.Add(new { Path = path, Type = "[FILE]", Name = filename_html });
|
|
}
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageExtrasViewer.RenderAsync(new { Extras = paths, Path = $"/{__path}", Parent = __path, Editable = me != null })));
|
|
}
|
|
private async Task ExtrasAlbumPageAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,out var cookie);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
if (!ctx.QueryParams.TryGetFirst("path", out var __path))
|
|
{
|
|
__path = "/";
|
|
}
|
|
__path = __path.TrimStart('/');
|
|
|
|
__path = SanitizePath(__path);
|
|
string extrasPath = Path.Combine(path, user, "album", album, "extras");
|
|
|
|
if (__path.Length > 0)
|
|
{
|
|
extrasPath = Path.Combine(extrasPath, __path);
|
|
}
|
|
|
|
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" });
|
|
}
|
|
if (Directory.Exists(extrasPath))
|
|
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 });
|
|
}
|
|
if (Directory.Exists(extrasPath))
|
|
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}/album/{album}/extras/{System.Web.HttpUtility.UrlPathEncode(__path.TrimEnd('/') + '/' + filename)}";
|
|
paths.Add(new { Path = path, Type = "[FILE]", Name = filename_html });
|
|
}
|
|
string csrf="";
|
|
if(me != null)
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageExtrasViewer.RenderAsync(new {csrf, Extras = paths, Path = $"/{__path}", Parent = __path, Editable = me != null })));
|
|
}
|
|
|
|
|
|
private async Task ExtrasMoviePageAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string movie = moviePathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,out var cookie);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
if (!ctx.QueryParams.TryGetFirst("path", out var __path))
|
|
{
|
|
__path = "/";
|
|
}
|
|
__path = __path.TrimStart('/');
|
|
|
|
__path = SanitizePath(__path);
|
|
string extrasPath = Path.Combine(path, user, "movie", movie, "extras");
|
|
|
|
if (__path.Length > 0)
|
|
{
|
|
extrasPath = Path.Combine(extrasPath, __path);
|
|
}
|
|
|
|
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" });
|
|
}
|
|
if (Directory.Exists(extrasPath))
|
|
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 });
|
|
}
|
|
if (Directory.Exists(extrasPath))
|
|
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 });
|
|
}
|
|
string csrf="";
|
|
if(me != null)
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageExtrasViewer.RenderAsync(new {csrf, Extras = paths, Path = $"/{__path}", Parent = __path, Editable = me != null })));
|
|
}
|
|
|
|
private string SanitizePath(string path)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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 GenerateBittorentFileAlbumAsync(string user, string album)
|
|
{
|
|
var _album = provider.GetAlbum(user, album);
|
|
string albumDir = Path.Combine(this.path, user, "album", album);
|
|
Directory.CreateDirectory(albumDir);
|
|
string extrasDir = Path.Combine(albumDir, "extras");
|
|
PieceStream strm = new PieceStream();
|
|
//calculate without extras
|
|
List<string[]> paths = new List<string[]>();
|
|
int track = 1;
|
|
foreach (var item in _album.Tracks)
|
|
{
|
|
string name = $"{track.ToString("D2")} {_album.AlbumArtist} - {item}.flac";
|
|
if (File.Exists(Path.Combine(albumDir, name))) paths.Add(new string[] { name });
|
|
track++;
|
|
}
|
|
|
|
if (File.Exists(Path.Combine(albumDir, "thumbnail.jpg")))
|
|
paths.Add(new string[] { "thumbnail.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(albumDir, _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}/album/";
|
|
torrent.Info.PieceLength = PieceStream.PieceLength;
|
|
torrent.Info.Name = album;
|
|
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(albumDir, $"{album}.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(albumDir, _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}/album/";
|
|
torrent.Info.PieceLength = PieceStream.PieceLength;
|
|
torrent.Info.Name = album;
|
|
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(albumDir, $"{album}_withextras.torrent");
|
|
using (var file = File.Create(torrentFile))
|
|
{
|
|
await torrent.ToTorrentFile().WriteToStreamAsync(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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[]>();
|
|
if (File.Exists(Path.Combine(movieDir, $"{movie}.mp4")))
|
|
paths.Add(new string[] { $"{movie}.mp4" });
|
|
if (File.Exists(Path.Combine(movieDir, "thumbnail.jpg")))
|
|
paths.Add(new string[] { "thumbnail.jpg" });
|
|
if (File.Exists(Path.Combine(movieDir, "poster.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");
|
|
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);
|
|
}
|
|
}
|
|
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,true);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
if (me != null)
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(path, user, "show", show, $"Season {seasonNo.ToString("D2")}"));
|
|
var tmpFile = Path.Combine(path, user, "show", show, $"Season {seasonNo.ToString("D2")}", $"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.ToString("D2")}", "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.ToString("D2")}", "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(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
private async Task UploadShowStreamAsync(ServerContext ctx)
|
|
{
|
|
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
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(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
|
|
private async Task GenerateBittorentFileShowAsync(string user, string show)
|
|
{
|
|
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.ToString("D2")}";
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private async Task UploadAlbumStreamAsync(ServerContext ctx)
|
|
{
|
|
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
var _movie = provider.GetAlbum(user, album);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
if (me != null)
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(path, user, "album", album));
|
|
var tmpFile = Path.Combine(path, user, "album", album, $"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
|
|
foreach (var item in ctx.ParseBody((n, fn, ct) => File.Create(tmpFile)))
|
|
item.Value.Dispose();
|
|
|
|
if (_movie != null)
|
|
{
|
|
|
|
string thumb = Path.Combine(path, user, "album", album, "thumbnail.jpg");
|
|
if (File.Exists(thumb)) File.Delete(thumb);
|
|
File.Move(tmpFile, thumb);
|
|
|
|
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileAlbumAsync(user, album);
|
|
});
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
|
|
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
|
|
}
|
|
|
|
|
|
private async Task UploadMovieStreamAsync(ServerContext ctx)
|
|
{
|
|
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string movie = moviePathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
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":
|
|
string thumb = Path.Combine(path, user, "movie", movie, "thumbnail.jpg");
|
|
if (File.Exists(thumb)) File.Delete(thumb);
|
|
File.Move(tmpFile, thumb);
|
|
break;
|
|
case "poster":
|
|
string poster = Path.Combine(path, user, "movie", movie, "poster.jpg");
|
|
if (File.Exists(poster)) File.Delete(poster);
|
|
File.Move(tmpFile, poster);
|
|
break;
|
|
case "movie":
|
|
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")}\"");
|
|
break;
|
|
}
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileMovieAsync(user, movie);
|
|
});
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- 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 EditMoviePagePostAsync(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string movie = moviePathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,true);
|
|
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.Replace("\r", "");
|
|
provider.UpdateMovie(_movie);
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Success</h1><a href=\"./edit\"><- Back</a>"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>Failed</h1><a href=\"./edit\"><- Back</a>"));
|
|
}
|
|
public async Task EditMoviePageAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string movie = moviePathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,out var cookie);
|
|
var _movie = provider.GetMovie(user, movie);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
|
|
if (_movie != null)
|
|
{
|
|
string csrf="";
|
|
string csrf2="";
|
|
if(me != null)
|
|
{
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
csrf2 = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageEditMovieDetails.RenderAsync(new {csrf,csrf2, Propername = System.Web.HttpUtility.HtmlAttributeEncode(_movie.ProperName), Description = System.Web.HttpUtility.HtmlEncode(_movie.Description) })));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>You are unauthorized to edit this</h1>"));
|
|
}
|
|
}
|
|
|
|
public async Task EditAlbumPageAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string album = albumPathValueServer.GetValue(ctx);
|
|
var me = GetAccount(ctx,out var cookie);
|
|
var _album = provider.GetAlbum(user, album);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
|
|
if (me != null)
|
|
{
|
|
|
|
if (_album != null)
|
|
{
|
|
string csrf="";
|
|
string csrf2="";
|
|
if(me != null)
|
|
{
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
csrf2 = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
}
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageEditAlbumDetails.RenderAsync(new { Year = _album.Year, Propername = System.Web.HttpUtility.HtmlAttributeEncode(_album.ProperName), Albumartist = System.Web.HttpUtility.HtmlAttributeEncode(_album.AlbumArtist), Description = System.Web.HttpUtility.HtmlEncode(_album.Description) ,csrf,csrf2})));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, "<h1>You are unauthorized to edit this</h1>"));
|
|
}
|
|
}
|
|
private async Task PlayEpisodePageAsync(ServerContext ctx)
|
|
{
|
|
string user = usersPathValueServer.GetValue(ctx);
|
|
string show = showPathValueServer.GetValue(ctx);
|
|
string seasonS = seasonPathValueServer.GetValue(ctx);
|
|
string episodeS = episodePathValueServer.GetValue(ctx);
|
|
if (!int.TryParse(seasonS, out var season)) season = 1;
|
|
if (!int.TryParse(episodeS, out var episode)) episode = 1;
|
|
|
|
var _show = provider.GetShow(user, show);
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
|
|
|
|
object value;
|
|
if (_show != null)
|
|
{
|
|
|
|
var data = GetEpisodeContentMetaData(user, show, season, episode);
|
|
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 });
|
|
}
|
|
string thumb = data.ThumbnailUrl;
|
|
|
|
|
|
value = new
|
|
{
|
|
curepisode = episode,
|
|
curseason = season,
|
|
nextepisodeapi = $"{Configuration.Root.TrimEnd('/')}/api/v1/NextEpisode?user={user}&show={show}",
|
|
username = user,
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasmovie = true,
|
|
episodethumbnail = thumb,
|
|
episodename = _show.Name,
|
|
episodeposter = data.PosterUrl,
|
|
episodebrowserurl = data.BrowserStream,
|
|
subtitles
|
|
};
|
|
}
|
|
else
|
|
{
|
|
value = new
|
|
{
|
|
username = user,
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasmovie = false
|
|
};
|
|
}
|
|
|
|
await ctx.SendTextAsync(await pageWatchEpisode.RenderAsync(value));
|
|
|
|
}
|
|
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 = GetMovieContentMetaData(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 });
|
|
}
|
|
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,
|
|
subtitles
|
|
};
|
|
}
|
|
else
|
|
{
|
|
value = new
|
|
{
|
|
username = user,
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasmovie = false
|
|
};
|
|
}
|
|
|
|
await ctx.SendTextAsync(await pageWatchMovie.RenderAsync(value));
|
|
|
|
}
|
|
IEnumerable<Episode> GetEpisodesFromShows(IEnumerable<Show> shows)
|
|
{
|
|
foreach (var show in shows)
|
|
{
|
|
foreach (var episode in GetEpisodesFromShow(show))
|
|
yield return episode;
|
|
}
|
|
}
|
|
IEnumerable<Episode> GetEpisodesFromShow(Show show)
|
|
{
|
|
var user = provider.GetUserById(show.UserId);
|
|
int count = provider.SeasonCount(user.Username, show.Name);
|
|
for (int i = 1; i <= count; i++)
|
|
{
|
|
int count2 = provider.EpisodeCount(user.Username, show.Name, i);
|
|
for (int j = 1; j < count2; j++)
|
|
{
|
|
yield return provider.GetEpisode(user.Username, show.Name, i, j);
|
|
}
|
|
}
|
|
}
|
|
IEnumerable<Episode> GetEpisodesFromSeason(Show show, int season)
|
|
{
|
|
var user = provider.GetUserById(show.UserId);
|
|
int count2 = provider.EpisodeCount(user.Username, show.Name, season);
|
|
for (int j = 1; j <= count2; j++)
|
|
{
|
|
yield return provider.GetEpisode(user.Username, show.Name, season, j);
|
|
}
|
|
}
|
|
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,out var cookie);
|
|
|
|
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"));
|
|
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";
|
|
string csrf="";
|
|
if(me != null)
|
|
csrf = HttpUtility.UrlEncode(CreateCSRF(me.Id,cookie));
|
|
value = new
|
|
{
|
|
csrf,
|
|
extrasexists,
|
|
moviebrowserexists,
|
|
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>")
|
|
};
|
|
}
|
|
else
|
|
{
|
|
value = new
|
|
{
|
|
username = user,
|
|
rooturl = $"{Configuration.Root.TrimEnd('/')}/",
|
|
title = Configuration.Title,
|
|
hasmovie = false
|
|
};
|
|
}
|
|
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await pageMovie.RenderAsync(value)));
|
|
}
|
|
private async Task ApiPutMovieExtraAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("movie", out var movie))
|
|
{
|
|
|
|
var me = GetAccount(ctx,true);
|
|
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");
|
|
await ctx.ReadToFileAsync(tmpFile);
|
|
|
|
if (ctx.QueryParams.TryGetFirst("extra", out var extra))
|
|
{
|
|
var _path = SanitizePath($"{extra.TrimStart('/')}").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.SendJsonAsync(new { success = true });
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task ApiPutShowExtraAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("show", out var show))
|
|
{
|
|
var me = GetAccount(ctx,true);
|
|
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");
|
|
await ctx.ReadToFileAsync(tmpFile);
|
|
|
|
if (ctx.QueryParams.TryGetFirst("extra", out var extra))
|
|
{
|
|
var _path = SanitizePath($"{extra.TrimStart('/')}").TrimStart('/');
|
|
var _path2 = Path.Combine(path, user, "show", show, "extras", _path);
|
|
if (File.Exists(_path2))
|
|
File.Delete(_path2);
|
|
File.Move(tmpFile, _path2);
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileShowAsync(user, show);
|
|
});
|
|
await ctx.SendJsonAsync(new { success = true });
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
private async Task ApiPutAlbumExtraAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("album", out var album))
|
|
{
|
|
var me = GetAccount(ctx,true);
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
me = null;
|
|
}
|
|
if (me != null)
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(path, user, "album", album));
|
|
var tmpFile = Path.Combine(path, user, "album", album, $"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
|
|
await ctx.ReadToFileAsync(tmpFile);
|
|
|
|
if (ctx.QueryParams.TryGetFirst("extra", out var extra))
|
|
{
|
|
var _path = SanitizePath($"{extra.TrimStart('/')}").TrimStart('/');
|
|
var _path2 = Path.Combine(path, user, "album", album, "extras", _path);
|
|
if (File.Exists(_path2))
|
|
File.Delete(_path2);
|
|
File.Move(tmpFile, _path2);
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileAlbumAsync(user, album);
|
|
});
|
|
await ctx.SendJsonAsync(new { success = true });
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private IServer CreateSwagme()
|
|
{
|
|
SwagmeServer swagmeServer = new SwagmeServer();
|
|
swagmeServer.AbsoluteUrl = true;
|
|
swagmeServer.Add("/Branding",async(ctx)=>{
|
|
await ctx.SendJsonAsync(new {
|
|
title = Configuration.Title
|
|
});
|
|
},new SwagmeDocumentation("Branding for server"));
|
|
swagmeServer.Add("/Login", ApiLogin, new SwagmeDocumentation("Login to account", "<b>email</b>: the email of account</br><b>password</b>: the password of account</br><b>type</b>: json or cookie"), "POST", "Users");
|
|
swagmeServer.Add("/LoggedInUserAccount",LoggedInAccountAsync,new SwagmeDocumentation("Get logged in account via authorization"),"GET","Users");
|
|
swagmeServer.Add("/GetPublicUsers", ApiGetPublicUsers, new SwagmeDocumentation("Get all public users"), "GET", "Users");
|
|
swagmeServer.Add("/Updates", (ctx) =>
|
|
{
|
|
|
|
SendEvents events = new SendEvents();
|
|
void evthdlr(object data) { try { events.SendEvent(data); } catch (Exception ex) { _ = ex; } }
|
|
SendEvents += evthdlr;
|
|
|
|
ctx.ServerSentEvents(events);
|
|
SendEvents -= evthdlr;
|
|
|
|
}, new SwagmeDocumentation("Server sent events for updates"), "GET", "Users");
|
|
swagmeServer.Add("/MovieFile", ApiPutMovieFileAsync, new SwagmeDocumentation("Put the movie resource"), "PUT", "Movies");
|
|
swagmeServer.Add("/MovieFile", ApiMovieFileAsync, new SwagmeDocumentation("Get the movie resource"), "GET", "Movies");
|
|
swagmeServer.Add("/MovieExtra", ApiPutMovieExtraAsync, new SwagmeDocumentation("Put the movie extra"), "PUT", "Movies");
|
|
swagmeServer.Add("/GetMovies", ApiGetMoviesAsync, new SwagmeDocumentation("Get a list of movies", "<b>user</b>: the user of the movies<br><b>type</b>: format of list (defaults to json): pls, wiimc, json, m3u8 or rss"), "GET", "Movies");
|
|
|
|
swagmeServer.Add("/ShowExtra", ApiPutShowExtraAsync, new SwagmeDocumentation("Put the show extra"), "PUT", "Shows");
|
|
swagmeServer.Add("/NextEpisode", ApiGetNextEpisodeAsync, new SwagmeDocumentation("Get the next episode if available", "<b>user</b>: the user of the show<br><b>show</b>: the show name<br><b>season</b>: the season number starting from 1<br><b>episode</b>: the episode number starting from 1"), "GET", "Shows");
|
|
swagmeServer.Add("/GetShows", ApiGetShowsAsync, new SwagmeDocumentation("Get a list of shows", "<b>user</b>: the user of the shows<br><b>type</b>: format of list (defaults to json): pls, wiimc, json, m3u8 or rss"), "GET", "Shows");
|
|
swagmeServer.Add("/GetSeasons", ApiGetSeasonsAsync, new SwagmeDocumentation("Get a list of episodes", "<b>user</b>: the user of the show</br><b>show</b>: the show</br><b>type</b>: format of list (defaults to json): pls, wiimc, json, m3u8 or rss"), "GET", "Shows");
|
|
swagmeServer.Add("/GetEpisodes", ApiGetEpisodesAsync, new SwagmeDocumentation("Get a list of episodes", "<b>user</b>: the user of the show</br><b>show</b>: the show</br><b>season</b>: the season<br><b>type</b>: format of list (defaults to json): pls, wiimc, json, m3u8 or rss"), "GET", "Shows");
|
|
swagmeServer.Add("/ShowFile", ApiShowFileAsync, new SwagmeDocumentation("Get the show resource"), "GET", "Shows");
|
|
swagmeServer.Add("/SeasonFile", ApiSeasonFileAsync, new SwagmeDocumentation("Get the season resource"), "GET", "Shows");
|
|
swagmeServer.Add("/EpisodeFile", ApiEpisodeFileAsync, new SwagmeDocumentation("Get the episode resource"), "GET", "Shows");
|
|
|
|
swagmeServer.Add("/AlbumFile", ApiAlbumFileAsync, new SwagmeDocumentation(""), "GET", "Albums");
|
|
swagmeServer.Add("/AlbumFile", ApiPutAlbumFileAsync, new SwagmeDocumentation("Put the album resource"), "PUT", "Albums");
|
|
swagmeServer.Add("/AlbumExtra", ApiPutAlbumExtraAsync, new SwagmeDocumentation("Put the album extra"), "PUT", "Albums");
|
|
swagmeServer.Add("/GetAlbums", ApiGetAlbumsAsync, new SwagmeDocumentation("Get a list of albums", "<b>user</b>: the user of the albums<br><b>type</b>: format of list (defaults to json): json or rss"), "GET", "Albums");
|
|
return swagmeServer;
|
|
}
|
|
|
|
private async Task LoggedInAccountAsync(ServerContext ctx)
|
|
{
|
|
var account = this.GetAccount(ctx);
|
|
if(account is null)
|
|
{
|
|
await ctx.SendJsonAsync(new LoggedInUserAccount{LoggedIn=false});
|
|
}
|
|
else
|
|
{
|
|
await ctx.SendJsonAsync(new LoggedInUserAccount{LoggedIn=true,UserName = account.Username, ProperName = account.ProperName, IsAdmin = account.IsAdmin, IsInvited = account.IsInvited, IsVerified=account.IsVerified});
|
|
}
|
|
}
|
|
|
|
private async Task ApiLogin(ServerContext ctx)
|
|
{
|
|
ctx.ParseBody();
|
|
|
|
if (ctx.QueryParams.TryGetFirst("email", out var email) && ctx.QueryParams.TryGetFirst("password", out var password) && ctx.QueryParams.TryGetFirst("type", out var type) && (type == "json" || type == "cookie"))
|
|
{
|
|
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 (provider.ContainsSession(cookie));
|
|
|
|
provider.CreateSession(cookie, a.Id);
|
|
if (type == "cookie")
|
|
{
|
|
ctx.ResponseHeaders.Add("Set-Cookie", $"Session={cookie}; Path=/");
|
|
await ctx.SendJsonAsync(new { success = true });
|
|
}
|
|
else if (type == "json")
|
|
{
|
|
await ctx.SendJsonAsync(new { success = true, cookie });
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
await ctx.SendJsonAsync(new { success = false });
|
|
}
|
|
|
|
private async Task ApiGetPublicUsers(ServerContext ctx)
|
|
{
|
|
List<UserAccount> users = new List<UserAccount>();
|
|
foreach (var user in provider.GetUsers())
|
|
{
|
|
if (!user.IsAdmin && Configuration.Publish == CMSPublish.Admin)
|
|
{
|
|
//await ctx.SendTextAsync(await RenderHtmlAsync(ctx,"<h1>You can't upload content</h1>"));
|
|
continue;
|
|
}
|
|
if (!(user.IsAdmin || user.IsInvited) && Configuration.Publish == CMSPublish.RequireInvite)
|
|
{
|
|
//await ctx.SendTextAsync(await RenderHtmlAsync(ctx,"<h1>You can't upload content</h1>"));
|
|
continue;
|
|
}
|
|
if (!(user.IsAdmin || user.IsInvited || user.IsVerified))
|
|
{
|
|
//await ctx.SendTextAsync(await RenderHtmlAsync(ctx,"<h1>You can't upload content</h1>"));
|
|
continue;
|
|
}
|
|
|
|
users.Add(user);
|
|
}
|
|
await ctx.SendJsonAsync(users);
|
|
}
|
|
|
|
private async Task ApiGetShowsAsync(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();
|
|
user = a.Username;
|
|
}
|
|
|
|
List<Show> shows = new List<Show>();
|
|
|
|
if (a != null)
|
|
{
|
|
shows.AddRange(provider.GetShows(a.Username));
|
|
}
|
|
|
|
if (type == "json")
|
|
{
|
|
await ctx.SendJsonAsync(shows);
|
|
}
|
|
if (type == "pls" || type == "wiimc")
|
|
{
|
|
var episodes = GetEpisodesFromShows(shows);
|
|
StringBuilder b = new StringBuilder();
|
|
b.AppendLine("[Playlist]");
|
|
int i = 1;
|
|
foreach (var item in episodes)
|
|
{
|
|
var show = provider.GetShow(item.UserId, item.ShowId);
|
|
if (type == "wiimc")
|
|
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/content/{HttpUtility.UrlEncode(user)}/show/{show.Name}/Season%20{item.SeasonNumber.ToString("D2")}/S{item.SeasonNumber.ToString("D2")}E{item.EpisodeNumber.ToString("D2")}.mp4");
|
|
else
|
|
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/api/v1/EpisodeFile?user={HttpUtility.UrlEncode(user)}&show={HttpUtility.UrlEncode(show.Name)}&season={item.SeasonNumber}&episode={item.EpisodeNumber}&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")
|
|
{
|
|
var episodes = GetEpisodesFromShows(shows);
|
|
M3uPlaylist playlist = new M3uPlaylist();
|
|
foreach (var item in episodes)
|
|
{
|
|
|
|
M3uPlaylistEntry entry = new M3uPlaylistEntry();
|
|
var show = provider.GetShow(item.UserId, item.ShowId);
|
|
entry.Path = $"{Configuration.Root.TrimEnd('/')}/api/v1/EpisodeFile?user={HttpUtility.UrlEncode(user)}&show={HttpUtility.UrlEncode(show.Name)}&season={item.SeasonNumber}&episode={item.EpisodeNumber}&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")
|
|
{
|
|
var episodes = GetEpisodesFromShows(shows);
|
|
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 episodes)
|
|
{
|
|
var show = provider.GetShow(item.UserId, item.ShowId);
|
|
AtomEntry entry = new AtomEntry();
|
|
entry.Title = item.ProperName;
|
|
entry.Description = $"View <a href=\"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{HttpUtility.UrlEncode(show.Name)}/season/{item.SeasonNumber}/episode/{item.EpisodeNumber}/\">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");
|
|
}
|
|
}
|
|
public async Task ApiGetSeasonsAsync(ServerContext ctx)
|
|
{
|
|
UserAccount a;
|
|
Show s;
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type)) type = "json";
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("show", out var show))
|
|
{
|
|
a = provider.GetUserAccount(user);
|
|
s = provider.GetShow(user, show);
|
|
}
|
|
else
|
|
{
|
|
ctx.StatusCode = 404;
|
|
await ctx.WriteHeadersAsync();
|
|
return;
|
|
}
|
|
|
|
|
|
if (type == "json")
|
|
{
|
|
List<Season> seasons = new List<Season>();
|
|
for (int i = 1; i <= provider.SeasonCount(s.UserId, s.Id); i++)
|
|
{
|
|
var season = provider.GetSeason(s.UserId, s.Id, i);
|
|
if (season != null)
|
|
seasons.Add(season);
|
|
}
|
|
await ctx.SendJsonAsync(seasons);
|
|
}
|
|
if (type == "pls" || type == "wiimc")
|
|
{
|
|
var episodes = GetEpisodesFromShow(s);
|
|
StringBuilder b = new StringBuilder();
|
|
b.AppendLine("[Playlist]");
|
|
int i = 1;
|
|
foreach (var item in episodes)
|
|
{
|
|
|
|
if (type == "wiimc")
|
|
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/content/{HttpUtility.UrlEncode(user)}/show/{s.Name}/Season%20{item.SeasonNumber.ToString("D2")}/S{item.SeasonNumber.ToString("D2")}E{item.EpisodeNumber.ToString("D2")}.mp4");
|
|
else
|
|
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/api/v1/EpisodeFile?user={HttpUtility.UrlEncode(user)}&show={HttpUtility.UrlEncode(s.Name)}&season={item.SeasonNumber}&episode={item.EpisodeNumber}&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")
|
|
{
|
|
var episodes = GetEpisodesFromShow(s);
|
|
M3uPlaylist playlist = new M3uPlaylist();
|
|
foreach (var item in episodes)
|
|
{
|
|
|
|
M3uPlaylistEntry entry = new M3uPlaylistEntry();
|
|
entry.Path = $"{Configuration.Root.TrimEnd('/')}/api/v1/EpisodeFile?user={HttpUtility.UrlEncode(user)}&show={HttpUtility.UrlEncode(s.Name)}&season={item.SeasonNumber}&episode={item.EpisodeNumber}&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")
|
|
{
|
|
var episodes = GetEpisodesFromShow(s);
|
|
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 episodes)
|
|
{
|
|
AtomEntry entry = new AtomEntry();
|
|
entry.Title = item.ProperName;
|
|
entry.Description = $"View <a href=\"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{HttpUtility.UrlEncode(s.Name)}/season/{item.SeasonNumber}/episode/{item.EpisodeNumber}/\">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");
|
|
}
|
|
}
|
|
public async Task ApiGetEpisodesAsync(ServerContext ctx)
|
|
{
|
|
UserAccount a;
|
|
Show s;
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type)) type = "json";
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("show", out var show) && ctx.QueryParams.TryGetFirstInt32("season", out var season))
|
|
{
|
|
a = provider.GetUserAccount(user);
|
|
s = provider.GetShow(user, show);
|
|
}
|
|
else
|
|
{
|
|
ctx.StatusCode = 404;
|
|
await ctx.WriteHeadersAsync();
|
|
return;
|
|
}
|
|
|
|
|
|
if (type == "json")
|
|
{
|
|
List<Episode> episodes = new List<Episode>();
|
|
for (int i = 1; i <= provider.EpisodeCount(s.UserId, s.Id, season); i++)
|
|
{
|
|
var episode = provider.GetEpisode(s.UserId, s.Id, season, i);
|
|
if (episode != null)
|
|
episodes.Add(episode);
|
|
}
|
|
await ctx.SendJsonAsync(episodes);
|
|
}
|
|
if (type == "pls" || type == "wiimc")
|
|
{
|
|
var episodes = GetEpisodesFromSeason(s, season);
|
|
StringBuilder b = new StringBuilder();
|
|
b.AppendLine("[Playlist]");
|
|
int i = 1;
|
|
foreach (var item in episodes)
|
|
{
|
|
|
|
if (type == "wiimc")
|
|
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/content/{HttpUtility.UrlEncode(user)}/show/{s.Name}/Season%20{item.SeasonNumber.ToString("D2")}/S{item.SeasonNumber.ToString("D2")}E{item.EpisodeNumber.ToString("D2")}.mp4");
|
|
else
|
|
b.AppendLine($"File{i}={Configuration.Root.TrimEnd('/')}/api/v1/EpisodeFile?user={HttpUtility.UrlEncode(user)}&show={HttpUtility.UrlEncode(s.Name)}&season={item.SeasonNumber}&episode={item.EpisodeNumber}&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")
|
|
{
|
|
var episodes = GetEpisodesFromSeason(s, season);
|
|
M3uPlaylist playlist = new M3uPlaylist();
|
|
foreach (var item in episodes)
|
|
{
|
|
|
|
M3uPlaylistEntry entry = new M3uPlaylistEntry();
|
|
entry.Path = $"{Configuration.Root.TrimEnd('/')}/api/v1/EpisodeFile?user={HttpUtility.UrlEncode(user)}&show={HttpUtility.UrlEncode(s.Name)}&season={item.SeasonNumber}&episode={item.EpisodeNumber}&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")
|
|
{
|
|
var episodes = GetEpisodesFromSeason(s, season);
|
|
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 episodes)
|
|
{
|
|
AtomEntry entry = new AtomEntry();
|
|
entry.Title = item.ProperName;
|
|
entry.Description = $"View <a href=\"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{HttpUtility.UrlEncode(s.Name)}/season/{item.SeasonNumber}/episode/{item.EpisodeNumber}/\">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 async Task ApiGetNextEpisodeAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("show", out var show) && ctx.QueryParams.TryGetFirst("season", out var seasonS) && ctx.QueryParams.TryGetFirst("episode", out var episodeS))
|
|
{
|
|
if (!int.TryParse(seasonS, out var season)) season = 1;
|
|
if (!int.TryParse(episodeS, out var episode)) episode = 1;
|
|
|
|
var _show = provider.GetShow(user, show);
|
|
var _episode = provider.GetEpisode(user, show, season, episode);
|
|
|
|
|
|
bool hasnextepisode = false;
|
|
|
|
int nextepisodeep = episode + 1;
|
|
int nextepisodeseason = season;
|
|
while (nextepisodeseason <= provider.SeasonCount(user, show))
|
|
{
|
|
|
|
var daseason = provider.GetSeason(user, show, nextepisodeseason);
|
|
if (daseason != null)
|
|
{
|
|
while (nextepisodeep <= provider.EpisodeCount(user, show, nextepisodeseason))
|
|
{
|
|
if (File.Exists(Path.Combine(path, user, "show", show, $"Season {nextepisodeseason.ToString("D2")}", $"S{nextepisodeseason.ToString("D2")}E{nextepisodeep.ToString("D2")}.mp4")))
|
|
{
|
|
hasnextepisode = true;
|
|
break;
|
|
}
|
|
|
|
nextepisodeep++;
|
|
}
|
|
}
|
|
if (hasnextepisode)
|
|
{
|
|
break;
|
|
}
|
|
nextepisodeep = 1;
|
|
nextepisodeseason++;
|
|
}
|
|
|
|
if (hasnextepisode)
|
|
{
|
|
var _epn = provider.GetEpisode(user, show, nextepisodeseason, nextepisodeep);
|
|
string next_episode_page_url = $"{Configuration.Root.TrimEnd('/')}/user/{user}/show/{show}/season/{nextepisodeseason}/episode/{nextepisodeep}/play";
|
|
var data = GetEpisodeContentMetaData(user, show, nextepisodeseason, nextepisodeep);
|
|
StringBuilder b = new StringBuilder();
|
|
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;
|
|
}
|
|
}
|
|
b.Append($"<track label=\"{languageName}\" kind=\"subtitles\" srclang=\"{langCode}\" src=\"{subtitle.VttUrl}\"/>");
|
|
|
|
}
|
|
await ctx.SendJsonAsync(new { next_subtitles_html = b.ToString(), has_next_episode = true, next_episode_episode = nextepisodeep, next_episode_season = nextepisodeseason, next_episode_name = _epn.EpisodeName, next_episode_url = data.BrowserStream, next_poster_url = data.PosterUrl, next_episode_page_url });
|
|
return;
|
|
}
|
|
}
|
|
await ctx.SendJsonAsync(new { has_next_episode = false });
|
|
}
|
|
private async Task ApiPutAlbumFileAsync(ServerContext ctx)
|
|
{
|
|
try
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("album", out var album))
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type))
|
|
type = "track";
|
|
|
|
if (!ctx.QueryParams.TryGetFirstInt32("track_id", out var track_id))
|
|
track_id = 1;
|
|
|
|
var me = GetAccount(ctx,true);
|
|
|
|
var _album = provider.GetAlbum(user, album);
|
|
|
|
if (me != null && me.Username != user && !me.IsAdmin)
|
|
{
|
|
|
|
me = null;
|
|
}
|
|
if (me != null)
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(path, user, "album", album));
|
|
var tmpFile = Path.Combine(path, user, "album", album, $"tmp{DateTime.Now.ToFileTime().ToString()}.bin");
|
|
await ctx.ReadToFileAsync(tmpFile);
|
|
|
|
if (_album != null)
|
|
{
|
|
|
|
switch (type)
|
|
{
|
|
case "thumbnail":
|
|
string thumb = Path.Combine(path, user, "album", album, "thumbnail.jpg");
|
|
if (File.Exists(thumb)) File.Delete(thumb);
|
|
File.Move(tmpFile, thumb);
|
|
break;
|
|
case "poster":
|
|
string poster = Path.Combine(path, user, "album", album, "poster.jpg");
|
|
if (File.Exists(poster)) File.Delete(poster);
|
|
File.Move(tmpFile, poster);
|
|
break;
|
|
case "track":
|
|
string oldtrack = _album.Tracks[track_id];
|
|
string flac = Path.Combine(path, user, "album", album, $"{(track_id + 1).ToString("D2")} {_album.AlbumArtist} - {oldtrack}.flac");
|
|
|
|
File.Move(tmpFile, flac);
|
|
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileAlbumAsync(user, album);
|
|
});
|
|
|
|
string oldmp3 = Path.Combine(path, user, "album", album, $"{oldtrack}.mp3");
|
|
|
|
ScheduleFFmpeg($"-y -i \"{flac}\" {Configuration.BrowserTranscodeMp3} \"{oldmp3}\"");
|
|
|
|
break;
|
|
}
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileAlbumAsync(user, album);
|
|
});
|
|
await ctx.SendJsonAsync(new { success = true });
|
|
return;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex);
|
|
}
|
|
}
|
|
|
|
private async Task ApiPutMovieFileAsync(ServerContext ctx)
|
|
{
|
|
try
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("movie", out var movie))
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type))
|
|
type = "movie";
|
|
|
|
var me = GetAccount(ctx,true);
|
|
|
|
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");
|
|
await ctx.ReadToFileAsync(tmpFile);
|
|
|
|
if (_movie != null)
|
|
{
|
|
|
|
switch (type)
|
|
{
|
|
case "thumbnail":
|
|
string thumb = Path.Combine(path, user, "movie", movie, "thumbnail.jpg");
|
|
if (File.Exists(thumb)) File.Delete(thumb);
|
|
File.Move(tmpFile, thumb);
|
|
break;
|
|
case "poster":
|
|
string poster = Path.Combine(path, user, "movie", movie, "poster.jpg");
|
|
if (File.Exists(poster)) File.Delete(poster);
|
|
File.Move(tmpFile, poster);
|
|
break;
|
|
case "movie":
|
|
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")}\"");
|
|
break;
|
|
}
|
|
ScheduleTask(async () =>
|
|
{
|
|
await GenerateBittorentFileMovieAsync(user, movie);
|
|
});
|
|
await ctx.SendJsonAsync(new { success = true });
|
|
return;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex);
|
|
}
|
|
}
|
|
private async Task ApiAlbumFileAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("album", out var album))
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type))
|
|
type = "json";
|
|
|
|
var info = GetAlbumContentMetadata(user, album);
|
|
switch (type)
|
|
{
|
|
case "thumbnail":
|
|
await ctx.SendRedirectAsync(info.ThumbnailUrl);
|
|
break;
|
|
case "poster":
|
|
await ctx.SendRedirectAsync(info.PosterUrl);
|
|
break;
|
|
case "torrent":
|
|
await ctx.SendRedirectAsync(info.AlbumTorrentUrl);
|
|
break;
|
|
case "torrent_extra":
|
|
await ctx.SendRedirectAsync(info.AlbumWithExtrasTorrentUrl);
|
|
break;
|
|
case "json":
|
|
await ctx.SendJsonAsync(info);
|
|
break;
|
|
case "wiimc":
|
|
case "pls":
|
|
{
|
|
StringBuilder b = new StringBuilder();
|
|
b.AppendLine("[Playlist]");
|
|
int i = 1;
|
|
foreach (var item in info.DownloadStreams)
|
|
{
|
|
|
|
b.AppendLine($"File{i}={IniEscape(item.Url)}");
|
|
b.AppendLine($"Length{i}=0");
|
|
b.AppendLine($"Title{i}={IniEscape(item.Name)}");
|
|
i++;
|
|
}
|
|
await ctx.SendTextAsync(b.ToString(), "audio/x-scpls");
|
|
}
|
|
break;
|
|
case "m3u8":
|
|
{
|
|
M3uPlaylist playlist = new M3uPlaylist();
|
|
foreach (var item in info.DownloadStreams)
|
|
{
|
|
M3uPlaylistEntry entry = new M3uPlaylistEntry();
|
|
entry.Path = item.Url;
|
|
entry.Title = item.Name;
|
|
playlist.PlaylistEntries.Add(entry);
|
|
}
|
|
M3uContent content = new M3uContent();
|
|
await ctx.SendTextAsync(content.ToText(playlist), "application/x-mpegurl");
|
|
}
|
|
break;
|
|
case "browser":
|
|
{
|
|
if (ctx.QueryParams.TryGetFirstInt32("track_number", out var track_no))
|
|
{
|
|
var res = info.BrowserStreams.Find(e => e.TrackNumber == track_no);
|
|
if (res != null)
|
|
await ctx.SendRedirectAsync(res.Url);
|
|
else
|
|
await ctx.SendNotFoundAsync();
|
|
}
|
|
else
|
|
await ctx.SendNotFoundAsync();
|
|
}
|
|
break;
|
|
case "download":
|
|
{
|
|
if (ctx.QueryParams.TryGetFirstInt32("track_number", out var track_no))
|
|
{
|
|
var res = info.DownloadStreams.Find(e => e.TrackNumber == track_no);
|
|
if (res != null)
|
|
await ctx.SendRedirectAsync(res.Url);
|
|
else
|
|
await ctx.SendNotFoundAsync();
|
|
}
|
|
else
|
|
await ctx.SendNotFoundAsync();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = GetMovieContentMetaData(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 ApiEpisodeFileAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("show", out var show) && ctx.QueryParams.TryGetFirstInt32("season", out var season) && ctx.QueryParams.TryGetFirstInt32("episode", out var episode))
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type))
|
|
type = "download";
|
|
|
|
var info = GetEpisodeContentMetaData(user, show, season, episode);
|
|
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 "json":
|
|
await ctx.SendJsonAsync(info);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
private async Task ApiSeasonFileAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("show", out var show) && ctx.QueryParams.TryGetFirstInt32("season", out var season))
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type))
|
|
type = "json";
|
|
|
|
var info = GetSeasonContentMetaData(user, show, season);
|
|
switch (type)
|
|
{
|
|
case "thumbnail":
|
|
await ctx.SendRedirectAsync(info.ThumbnailUrl);
|
|
break;
|
|
case "poster":
|
|
await ctx.SendRedirectAsync(info.PosterUrl);
|
|
break;
|
|
case "json":
|
|
await ctx.SendJsonAsync(info);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task ApiShowFileAsync(ServerContext ctx)
|
|
{
|
|
if (ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("show", out var show))
|
|
{
|
|
if (!ctx.QueryParams.TryGetFirst("type", out var type))
|
|
type = "json";
|
|
|
|
var info = GetShowContentMetaData(user, show);
|
|
switch (type)
|
|
{
|
|
case "thumbnail":
|
|
await ctx.SendRedirectAsync(info.ThumbnailUrl);
|
|
break;
|
|
case "poster":
|
|
await ctx.SendRedirectAsync(info.PosterUrl);
|
|
break;
|
|
case "torrent":
|
|
await ctx.SendRedirectAsync(info.ShowTorrentUrl);
|
|
break;
|
|
case "torrent_extra":
|
|
await ctx.SendRedirectAsync(info.ShowWithExtrasTorrentUrl);
|
|
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();
|
|
user = a.Username;
|
|
}
|
|
|
|
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" || type == "wiimc")
|
|
{
|
|
StringBuilder b = new StringBuilder();
|
|
b.AppendLine("[Playlist]");
|
|
int i = 1;
|
|
foreach (var item in movies)
|
|
{
|
|
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");
|
|
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 async Task ApiGetAlbumsAsync(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();
|
|
user = a.Username;
|
|
}
|
|
|
|
List<Album> albums = new List<Album>();
|
|
|
|
if (a != null)
|
|
{
|
|
albums.AddRange(provider.GetAlbums(a.Id));
|
|
}
|
|
|
|
if (type == "json")
|
|
{
|
|
await ctx.SendJsonAsync(albums);
|
|
}
|
|
|
|
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 Albums");
|
|
await rss.WriteGenerator("TessesCMS");
|
|
await rss.WriteValue("link", $"{Configuration.Root.TrimEnd('/')}/");
|
|
foreach (var item in albums)
|
|
{
|
|
AtomEntry entry = new AtomEntry();
|
|
entry.Title = item.ProperName;
|
|
entry.Description = $"View <a href=\"{Configuration.Root.TrimEnd('/')}/user/{user}/album/{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(ServerContext ctx, string body, bool isHome = false, bool isDevcenter = false, bool isLogin = false, bool isUpload = false)
|
|
{
|
|
List<object> _urls = new List<object>();
|
|
var account = GetAccount(ctx);
|
|
CMSNavUrl accountLink = account != null ? Configuration.RelativeNavUrl(account.ProperName, "account") : Configuration.RelativeNavUrl("Login", "login");
|
|
accountLink.Active = isLogin;
|
|
var dc = Configuration.RelativeNavUrl("Devcenter", "devcenter");
|
|
dc.Active = isDevcenter;
|
|
var upload = Configuration.RelativeNavUrl("Upload", "upload");
|
|
upload.Active = isUpload;
|
|
_urls.Add(upload);
|
|
_urls.Add(dc);
|
|
_urls.Add(accountLink);
|
|
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)
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await RenderIndexAsync(), true));
|
|
}
|
|
|
|
private async Task Devcenter(ServerContext ctx)
|
|
{
|
|
await ctx.SendTextAsync(await RenderHtmlAsync(ctx, await RenderDevcenterAsync(), false, true));
|
|
}
|
|
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(string csrf)
|
|
{
|
|
return await pageUpload.RenderAsync(new { title = Configuration.Title,csrf, rooturl = $"{Configuration.Root.TrimEnd('/')}/" });
|
|
|
|
}
|
|
public CMSConfiguration Configuration { get; set; } = new CMSConfiguration();
|
|
public IServer Server => new CSRFCatcherServer(routeServer);
|
|
}
|
|
|
|
internal class CSRFCatcherServer : IServer
|
|
{
|
|
IServer inside;
|
|
public CSRFCatcherServer(IServer server)
|
|
{
|
|
this.inside = server;
|
|
}
|
|
|
|
public async Task<bool> BeforeAsync(ServerContext ctx)
|
|
{
|
|
try
|
|
{
|
|
return await inside.BeforeAsync(ctx);
|
|
}
|
|
catch (InvalidCSRFException)
|
|
{
|
|
await ctx.SendTextAsync("<h1>CSRF token invalid</h1><p>Press back, refresh and try again</p>");
|
|
}
|
|
return true;
|
|
}
|
|
public async Task PostAsync(ServerContext ctx)
|
|
{
|
|
try
|
|
{
|
|
await inside.PostAsync(ctx);
|
|
}
|
|
catch (InvalidCSRFException)
|
|
{
|
|
await ctx.SendTextAsync("<h1>CSRF token invalid</h1><p>Press back, refresh and try again</p>");
|
|
}
|
|
}
|
|
public async Task OptionsAsync(ServerContext ctx)
|
|
{
|
|
try
|
|
{
|
|
await inside.OptionsAsync(ctx);
|
|
}
|
|
catch (InvalidCSRFException)
|
|
{
|
|
await ctx.SendTextAsync("<h1>CSRF token invalid</h1><p>Press back, refresh and try again</p>");
|
|
}
|
|
}
|
|
public async Task OtherAsync(ServerContext ctx)
|
|
{
|
|
try
|
|
{
|
|
await inside.OtherAsync(ctx);
|
|
}
|
|
catch (InvalidCSRFException)
|
|
{
|
|
await ctx.SendTextAsync("<h1>CSRF token invalid</h1><p>Press back, refresh and try again</p>");
|
|
}
|
|
}
|
|
public async Task GetAsync(ServerContext ctx)
|
|
{
|
|
try
|
|
{
|
|
await inside.GetAsync(ctx);
|
|
}
|
|
catch (InvalidCSRFException)
|
|
{
|
|
await ctx.SendTextAsync("<h1>CSRF token invalid</h1><p>Press back, refresh and try again</p>");
|
|
}
|
|
}
|
|
|
|
public void AddCors(ServerContext ctx)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
internal class InvalidCSRFException : Exception
|
|
{
|
|
public InvalidCSRFException()
|
|
{
|
|
}
|
|
|
|
public InvalidCSRFException(string message) : base(message)
|
|
{
|
|
}
|
|
|
|
public InvalidCSRFException(string message, Exception innerException) : base(message, innerException)
|
|
{
|
|
}
|
|
|
|
protected InvalidCSRFException(SerializationInfo info, StreamingContext context) : base(info, context)
|
|
{
|
|
}
|
|
}
|
|
|
|
public class EpisodeContentMetaData
|
|
{
|
|
[JsonProperty("episode_info")]
|
|
public Episode Info { get; set; }
|
|
[JsonProperty("has_browser_stream")]
|
|
public bool HasBrowserStream { get; set; }
|
|
[JsonProperty("has_download_stream")]
|
|
public bool HasDownloadStream { get; set; }
|
|
|
|
[JsonProperty("has_poster")]
|
|
public bool HasPoster { get; set; }
|
|
[JsonProperty("has_thumbnail")]
|
|
public bool HasThumbnail { get; set; }
|
|
|
|
[JsonProperty("browser_stream")]
|
|
public string BrowserStream { get; set; }
|
|
|
|
[JsonProperty("download_stream")]
|
|
public string DownloadStream { get; set; }
|
|
|
|
[JsonProperty("poster_url")]
|
|
public string PosterUrl { get; set; }
|
|
|
|
[JsonProperty("thumbnail_url")]
|
|
|
|
public string ThumbnailUrl { get; set; }
|
|
|
|
[JsonProperty("subtitle_streams")]
|
|
public List<SubtitleStream> SubtitlesStreams { get; set; } = new List<SubtitleStream>();
|
|
|
|
}
|
|
|
|
internal class EmailCreator
|
|
{
|
|
CMSConfiguration configuration;
|
|
Template emailTemplate;
|
|
Template verifyTemplate;
|
|
Template emailMovie;
|
|
Template emailShow;
|
|
Template emailAlbum;
|
|
public EmailCreator(CMSConfiguration configuration)
|
|
{
|
|
this.configuration = configuration;
|
|
emailTemplate = Template.Parse(AssetProvider.ReadAllText("/EmailHtml.html"));
|
|
verifyTemplate = Template.Parse(AssetProvider.ReadAllText("/VerifyEmail.html"));
|
|
emailMovie = Template.Parse(AssetProvider.ReadAllText("/EmailMovie.html"));
|
|
emailShow = Template.Parse(AssetProvider.ReadAllText("/EmailShow.html"));
|
|
emailAlbum = Template.Parse(AssetProvider.ReadAllText("/EmailAlbum.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}");
|
|
}
|
|
public async Task EmailShowAsync(IContentProvider provider, UserAccount account, Show show, bool update = false, string body = "")
|
|
{
|
|
foreach (var uAccount in account.AccountsToMail)
|
|
{
|
|
if (!uAccount.EnableShows) continue;
|
|
if (!uAccount.EnableShows && update) continue;
|
|
|
|
var user = provider.GetUserById(uAccount.UserId);
|
|
if (user != null)
|
|
{
|
|
bool hasmessage = false;
|
|
string message = "";
|
|
if (!string.IsNullOrWhiteSpace(body))
|
|
{
|
|
message = CMSServer.DescriptLinkUtils(body).Replace("\n", "<br>");
|
|
hasmessage = true;
|
|
}
|
|
await SendEmailAsync(user.Email, await emailShow.RenderAsync(new { hasmessage, message, propername = HttpUtility.HtmlEncode(user.ProperName), showuserproper = HttpUtility.HtmlEncode(account.ProperName), showproper = HttpUtility.HtmlEncode(show.ProperName), updated = update, showurl = $"{configuration.Root.TrimEnd('/')}/user/{account.Username}/show/{show.Name}/" }), $"{account.ProperName} {(update ? "updated" : "created")} the show {show.ProperName}");
|
|
}
|
|
}
|
|
}
|
|
public async Task EmailMovieAsync(IContentProvider provider, UserAccount account, Movie movie, bool update = false, string body = "")
|
|
{
|
|
foreach (var uAccount in account.AccountsToMail)
|
|
{
|
|
if (!uAccount.EnableMovies) continue;
|
|
if (!uAccount.EnableUpdates && update) continue;
|
|
|
|
var user = provider.GetUserById(uAccount.UserId);
|
|
if (user != null)
|
|
{
|
|
bool hasmessage = false;
|
|
string message = "";
|
|
if (!string.IsNullOrWhiteSpace(body))
|
|
{
|
|
message = CMSServer.DescriptLinkUtils(body).Replace("\n", "<br>");
|
|
hasmessage = true;
|
|
}
|
|
|
|
|
|
|
|
|
|
await SendEmailAsync(user.Email, await emailMovie.RenderAsync(new { hasmessage, message, propername = HttpUtility.HtmlEncode(user.ProperName), movieuserproper = HttpUtility.HtmlEncode(account.ProperName), movieproper = HttpUtility.HtmlEncode(movie.ProperName), updated = update, movieurl = $"{configuration.Root.TrimEnd('/')}/user/{account.Username}/movie/{movie.Name}/" }), $"{account.ProperName} {(update ? "updated" : "created")} the movie {movie.ProperName}");
|
|
}
|
|
}
|
|
}
|
|
|
|
internal async Task EmailAlbumAsync(IContentProvider provider, UserAccount account, Album album, bool update = false, string body = "")
|
|
{
|
|
foreach (var uAccount in account.AccountsToMail)
|
|
{
|
|
if (!uAccount.EnableMovies) continue;
|
|
if (!uAccount.EnableUpdates && update) continue;
|
|
|
|
var user = provider.GetUserById(uAccount.UserId);
|
|
if (user != null)
|
|
{
|
|
bool hasmessage = false;
|
|
string message = "";
|
|
if (!string.IsNullOrWhiteSpace(body))
|
|
{
|
|
message = CMSServer.DescriptLinkUtils(body).Replace("\n", "<br>");
|
|
hasmessage = true;
|
|
}
|
|
|
|
await SendEmailAsync(user.Email, await emailAlbum.RenderAsync(new { hasmessage, message, propername = HttpUtility.HtmlEncode(user.ProperName), albumuserproper = HttpUtility.HtmlEncode(account.ProperName), albumproper = HttpUtility.HtmlEncode(album.ProperName), updated = update, albumurl = $"{configuration.Root.TrimEnd('/')}/user/{account.Username}/album/{album.Name}/" }), $"{account.ProperName} {(update ? "updated" : "created")} the album {album.ProperName}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|