Shows almost done
This commit is contained in:
parent
52a0ba1f04
commit
f81fb656e8
|
@ -482,3 +482,4 @@ $RECYCLE.BIN/
|
||||||
|
|
||||||
# Vim temporary swap files
|
# Vim temporary swap files
|
||||||
*.swp
|
*.swp
|
||||||
|
help.txt
|
|
@ -0,0 +1,13 @@
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
WORKDIR /src/Tesses.CMS.Server
|
||||||
|
RUN dotnet publish -c Release -o /app
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/runtime:7.0 AS runner
|
||||||
|
RUN apt update && apt install -y ffmpeg
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app .
|
||||||
|
EXPOSE 62444
|
||||||
|
ENTRYPOINT ["dotnet", "Tesses.CMS.Server.dll", "/data"]
|
|
@ -1,17 +1,14 @@
|
||||||
using Tesses.CMS.Client;
|
using Tesses.CMS.Client;
|
||||||
|
|
||||||
TessesCMSClient client = new TessesCMSClient("http://192.168.0.155:62444/");
|
TessesCMSClient client = new TessesCMSClient("http://192.168.0.158:62444/");
|
||||||
|
|
||||||
await foreach(var item in client.Movies.GetMoviesAsync("Blender"))
|
await foreach(var item in client.Movies.GetMoviesAsync("tesses"))
|
||||||
{
|
{
|
||||||
|
var res=await client.Movies.GetMovieContentMetadataAsync("tesses",item.Name);
|
||||||
Console.WriteLine(item.ProperName);
|
Console.WriteLine(item.ProperName);
|
||||||
|
Console.WriteLine($"\t{res.PosterUrl}");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success=await client.Users.LoginAsync("johndoe@example.com","Se7enMovie");
|
|
||||||
if(success)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Logged in successfully");
|
|
||||||
}
|
|
||||||
//await client.Movies.CreateAsync("HolyLoop","Holy Loop");
|
//await client.Movies.CreateAsync("HolyLoop","Holy Loop");
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -41,6 +43,46 @@ namespace Tesses.CMS.Client
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
public async Task DownloadFileAsync(string url,string dest,CancellationToken token=default,IProgress<double> progress=null)
|
||||||
|
{
|
||||||
|
using(var f = File.Create(dest))
|
||||||
|
await DownloadFileAsync(url,f,token,progress);
|
||||||
|
}
|
||||||
|
public async Task DownloadFileAsync(string url,Stream dest,CancellationToken token=default,IProgress<double> progress=null)
|
||||||
|
{
|
||||||
|
|
||||||
|
long offset=0;
|
||||||
|
long total=0;
|
||||||
|
if(dest.CanSeek)
|
||||||
|
{
|
||||||
|
offset=dest.Length;
|
||||||
|
dest.Seek(offset,SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
HttpRequestMessage message=new HttpRequestMessage(HttpMethod.Get,url);
|
||||||
|
if(offset > 0)
|
||||||
|
{
|
||||||
|
message.Headers.Range=new System.Net.Http.Headers.RangeHeaderValue(offset,null);
|
||||||
|
|
||||||
|
}
|
||||||
|
var resp=await client.SendAsync(message);
|
||||||
|
if(resp.StatusCode == System.Net.HttpStatusCode.RequestedRangeNotSatisfiable) return;
|
||||||
|
resp.EnsureSuccessStatusCode();
|
||||||
|
total = resp.StatusCode == System.Net.HttpStatusCode.PartialContent ? resp.Content.Headers.ContentLength.GetValueOrDefault() + offset : resp.Content.Headers.ContentLength.GetValueOrDefault();
|
||||||
|
int read=0;
|
||||||
|
byte[] buffer=new byte[1024];
|
||||||
|
|
||||||
|
using(var srcStrm = await resp.Content.ReadAsStreamAsync())
|
||||||
|
do {
|
||||||
|
if(token.IsCancellationRequested) return;
|
||||||
|
read = await srcStrm.ReadAsync(buffer,0,buffer.Length,token);
|
||||||
|
if(token.IsCancellationRequested) return;
|
||||||
|
await dest.WriteAsync(buffer,0,read,token);
|
||||||
|
offset += read;
|
||||||
|
if(total > 0)
|
||||||
|
progress?.Report((double)offset / (double)read);
|
||||||
|
} while(read>0);
|
||||||
|
resp.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public MovieClient Movies => new MovieClient(this);
|
public MovieClient Movies => new MovieClient(this);
|
||||||
|
|
||||||
|
@ -81,5 +123,18 @@ namespace Tesses.CMS.Client
|
||||||
yield return item;
|
yield return item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task<MovieContentMetaData> GetMovieContentMetadataAsync(string user,string movie)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<MovieContentMetaData>(await client.client.GetStringAsync($"{client.rooturl.TrimEnd('/')}/api/v1/MovieFile?movie={movie}&user={user}&type=json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownloadMovieAsync(string user,string movie,string dest,CancellationToken token=default,IProgress<double> progress=null)
|
||||||
|
{
|
||||||
|
await client.DownloadFileAsync($"{client.rooturl.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.mp4",dest,token,progress);
|
||||||
|
}
|
||||||
|
public async Task DownloadMovieAsync(string user,string movie,Stream dest,CancellationToken token=default,IProgress<double> progress=null)
|
||||||
|
{
|
||||||
|
await client.DownloadFileAsync($"{client.rooturl.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.mp4",dest,token,progress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,59 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Tesses.CMS.Client
|
namespace Tesses.CMS.Client
|
||||||
{
|
{
|
||||||
|
public class MovieContentMetaData
|
||||||
|
{
|
||||||
|
[JsonProperty("movie_torrent_url")]
|
||||||
|
public string MovieTorrentUrl {get;set;}
|
||||||
|
|
||||||
|
[JsonProperty("movie_with_extras_torrent_url")]
|
||||||
|
public string MovieWithExtrasTorrentUrl {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>();
|
||||||
|
|
||||||
|
[JsonProperty("extra_streams")]
|
||||||
|
public List<ExtraDataStream> ExtraStreams {get;set;}=new List<ExtraDataStream>();
|
||||||
|
}
|
||||||
|
public class ExtraDataStream
|
||||||
|
{
|
||||||
|
[JsonProperty("is_dir")]
|
||||||
|
public bool IsDir {get;set;}
|
||||||
|
[JsonProperty("name")]
|
||||||
|
public string Name {get;set;}
|
||||||
|
|
||||||
|
[JsonProperty("items")]
|
||||||
|
|
||||||
|
public List<ExtraDataStream> Items {get;set;}=new List<ExtraDataStream>();
|
||||||
|
[JsonProperty("url")]
|
||||||
|
public string Url {get;set;}
|
||||||
|
}
|
||||||
|
public class SubtitleStream
|
||||||
|
{
|
||||||
|
[JsonProperty("language_code")]
|
||||||
|
public string LanguageCode {get;set;}="";
|
||||||
|
|
||||||
|
[JsonProperty("url")]
|
||||||
|
|
||||||
|
public string SubtitleUrl {get;set;}
|
||||||
|
}
|
||||||
|
|
||||||
public class Movie
|
public class Movie
|
||||||
{
|
{
|
||||||
public string ProperName {get;set;}
|
public string ProperName {get;set;}
|
||||||
|
|
|
@ -0,0 +1,298 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Tesses.CMS;
|
||||||
|
using Dapper;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
namespace Tesses.CMS.Providers
|
||||||
|
{
|
||||||
|
public class MysqlDapperContentProvider : IContentProvider
|
||||||
|
{
|
||||||
|
DbConnection con;
|
||||||
|
public MysqlDapperContentProvider(DbConnection connection)
|
||||||
|
{
|
||||||
|
this.con = connection;
|
||||||
|
|
||||||
|
con.Execute("CREATE TABLE IF NOT EXISTS Users (Id bigint NOT NULL PRIMARY KEY AUTOINCREMENT,AboutMe text,AccountsToMail text,Email text,IsAdmin bool,IsInvited bool,IsVerified bool,PasswordHash text,ProperName text,Salt text,Username text,Webhooks text);");
|
||||||
|
con.Execute("CREATE TABLE IF NOT EXISTS Sessions (Id bigint NOT NULL PRIMARY KEY AUTOINCREMENT,Session text,Account bigint);");
|
||||||
|
con.Execute("CREATE TABLE IF NOT EXISTS VerificationCodes (Id bigint NOT NULL PRIMARY KEY AUTOINCREMENT,Session text,Account bigint,Expires bigint);");
|
||||||
|
con.Execute("CREATE TABLE IF NOT EXISTS Movies (Id bigint NOT NULL PRIMARY KEY AUTOINCREMENT,UserId bigint,CreationTime bigint,LastUpdated bigint,Description text,Name text,ProperName text);");
|
||||||
|
|
||||||
|
}
|
||||||
|
public void ChangeSession(string session, long account)
|
||||||
|
{
|
||||||
|
con.Execute("UPDATE Sessions set Account = @account WHERE Session = @session;",new{session,account});
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsSession(string cookie)
|
||||||
|
{
|
||||||
|
return con.QueryFirstOrDefault("SELECT * FROM Sessions WHERE Session = @cookie;",new{cookie})!=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsVerificationCode(string code)
|
||||||
|
{
|
||||||
|
var res=con.QueryFirstOrDefault("SELECT * FROM VerificationCodes WHERE Session = @code;",new{code});
|
||||||
|
if(res != null)
|
||||||
|
{
|
||||||
|
long expires = res.Expires;
|
||||||
|
if(DateTimeOffset.Now>= DateTimeOffset.FromUnixTimeSeconds(expires))
|
||||||
|
{
|
||||||
|
con.Execute("DELETE FROM VerificationCodes WHERE Session = @code;",new{code});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Movie CreateMovie(string user, string movie, string properName, string description)
|
||||||
|
{
|
||||||
|
var userId=GetUserAccount(user).Id;
|
||||||
|
Movie movie1=new Movie();
|
||||||
|
movie1.UserId = userId;
|
||||||
|
movie1.Name = movie;
|
||||||
|
movie1.ProperName=properName;
|
||||||
|
movie1.Description=description;
|
||||||
|
|
||||||
|
con.Execute("INSERT INTO Movies(movie) values (@movie);",new{movie=new DapperMovie(movie1)});
|
||||||
|
return GetMovie(user,movie);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateSession(string session, long account)
|
||||||
|
{
|
||||||
|
con.Execute("INSERT INTO Sessions(Session,Account) values (@session,@account);",new{session,account});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateUser(CMSConfiguration configuration, string user, string properName, string email, string password)
|
||||||
|
{
|
||||||
|
bool first=con.QueryFirstOrDefault<DapperUserAccount>("SELECT * FROM Users LIMIT 0, 1;") == null;
|
||||||
|
|
||||||
|
UserAccount account=new UserAccount();
|
||||||
|
account.IsAdmin = first;
|
||||||
|
account.IsVerified = first;
|
||||||
|
account.Email =email;
|
||||||
|
account.ProperName = properName;
|
||||||
|
account.Username = user;
|
||||||
|
account.NewSalt();
|
||||||
|
account.PasswordHash = account.GetPasswordHash(password);
|
||||||
|
|
||||||
|
|
||||||
|
DapperUserAccount account1=new DapperUserAccount(account);
|
||||||
|
|
||||||
|
con.Execute("INSERT INTO Users(user) values (@user);",new{user=account1});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateVerificationCode(string code, long account)
|
||||||
|
{
|
||||||
|
long expires=DateTimeOffset.Now.AddDays(1).ToUnixTimeSeconds();
|
||||||
|
con.Execute("INSERT INTO VerificationCodes(Session,Account,Expires) values (@code,@account,@expires);",new{code,account,expires});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteSession(string session)
|
||||||
|
{
|
||||||
|
con.Execute("DELETE FROM Sessions WHERE Session = @session;",new{session});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteVerificationCode(string code)
|
||||||
|
{
|
||||||
|
con.Execute("DELETE FROM VerificationCodes WHERE Session = @code;",new{code});
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserAccount GetFirstUser()
|
||||||
|
{
|
||||||
|
return con.QueryFirstOrDefault<DapperUserAccount>("SELECT * FROM Users LIMIT 0, 1;")?.Account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Movie GetMovie(string user, string movie)
|
||||||
|
{
|
||||||
|
long? id =GetUserAccount(user)?.Id;
|
||||||
|
if(!id.HasValue) return null;
|
||||||
|
return con.QueryFirstOrDefault<DapperMovie>("SELECT * FROM Users WHERE UserId=@id; AND Name=@movie",new{movie,id})?.MovieObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<Movie> GetMovies(string user)
|
||||||
|
{
|
||||||
|
long? id =GetUserAccount(user)?.Id;
|
||||||
|
if(!id.HasValue) yield break;
|
||||||
|
foreach(var item in GetMovies(id.Value))
|
||||||
|
{
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Movie> GetMovies(long user)
|
||||||
|
{
|
||||||
|
foreach(var item in con.Query<DapperMovie>("SELECT * FROM Movies;"))
|
||||||
|
{
|
||||||
|
yield return item.MovieObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long? GetSession(string session)
|
||||||
|
{
|
||||||
|
return con.QueryFirstOrDefault("SELECT * FROM Sessions WHERE Session = @session;",new{session})?.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserAccount GetUserAccount(string user)
|
||||||
|
{
|
||||||
|
return con.QueryFirstOrDefault<DapperUserAccount>("SELECT * FROM Users WHERE Username=@user;",new{user})?.Account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserAccount GetUserById(long account)
|
||||||
|
{
|
||||||
|
return con.QueryFirstOrDefault<DapperUserAccount>("SELECT * FROM Users WHERE Id=@id;",new{id=account})?.Account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<UserAccount> GetUsers()
|
||||||
|
{
|
||||||
|
foreach(var user in con.Query<DapperUserAccount>("SELECT * FROM Users;"))
|
||||||
|
{
|
||||||
|
yield return user.Account;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long? GetVerificationAccount(string code)
|
||||||
|
{
|
||||||
|
var res=con.QueryFirstOrDefault("SELECT * FROM VerificationCodes WHERE Session = @code;",new{code});
|
||||||
|
if(res != null)
|
||||||
|
{
|
||||||
|
long expires = res.Expires;
|
||||||
|
if(DateTimeOffset.Now>= DateTimeOffset.FromUnixTimeSeconds(expires))
|
||||||
|
{
|
||||||
|
con.Execute("DELETE FROM VerificationCodes WHERE Session = @code;",new{code});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return res.Account;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateMovie(Movie movie)
|
||||||
|
{
|
||||||
|
DapperMovie dapperMovie=new DapperMovie(movie);
|
||||||
|
con.Execute("UPDATE Users set movie = @movie WHERE Id = @id;",new{movie=dapperMovie,id=movie.Id});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUser(UserAccount account)
|
||||||
|
{
|
||||||
|
DapperUserAccount account1=new DapperUserAccount(account);
|
||||||
|
con.Execute("UPDATE Users set user = @user WHERE Id = @id;",new{user=account1,id=account1.Id});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DapperMovie
|
||||||
|
{
|
||||||
|
public long Id {get;set;}
|
||||||
|
public long UserId {get;set;}
|
||||||
|
|
||||||
|
public long CreationTime {get;set;}
|
||||||
|
|
||||||
|
public long LastUpdated {get;set;}
|
||||||
|
|
||||||
|
public string Description {get;set;}
|
||||||
|
|
||||||
|
public string Name {get;set;}
|
||||||
|
|
||||||
|
public string ProperName {get;set;}
|
||||||
|
|
||||||
|
|
||||||
|
public Movie MovieObject {
|
||||||
|
get{
|
||||||
|
return new Movie(){
|
||||||
|
UserId = UserId,
|
||||||
|
CreationTime=DateTimeOffset.FromUnixTimeSeconds(CreationTime).DateTime,
|
||||||
|
LastUpdated=DateTimeOffset.FromUnixTimeSeconds(LastUpdated).DateTime,
|
||||||
|
Description=Description,
|
||||||
|
Id=Id,
|
||||||
|
Name=Name,
|
||||||
|
ProperName=ProperName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
set{
|
||||||
|
UserId=value.UserId;
|
||||||
|
CreationTime=new DateTimeOffset(value.CreationTime).ToUnixTimeSeconds();
|
||||||
|
LastUpdated=new DateTimeOffset(value.LastUpdated).ToUnixTimeSeconds();
|
||||||
|
Description=value.Description;
|
||||||
|
Id=value.Id;
|
||||||
|
Name=value.Name;
|
||||||
|
ProperName=value.ProperName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DapperMovie(Movie movie1)
|
||||||
|
{
|
||||||
|
MovieObject = movie1;
|
||||||
|
}
|
||||||
|
public DapperMovie()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DapperUserAccount
|
||||||
|
{
|
||||||
|
public DapperUserAccount()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public DapperUserAccount(UserAccount account)
|
||||||
|
{
|
||||||
|
Account =account;
|
||||||
|
}
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string AboutMe {get;set;}
|
||||||
|
public string AccountsToMail { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
public bool IsAdmin {get;set;}
|
||||||
|
public bool IsInvited {get;set;}
|
||||||
|
|
||||||
|
public bool IsVerified {get;set;}
|
||||||
|
|
||||||
|
public string PasswordHash {get;set;}
|
||||||
|
public string ProperName {get;set;}
|
||||||
|
public string Salt {get;set;}
|
||||||
|
public string Username {get;set;}
|
||||||
|
|
||||||
|
public string Webhooks {get;set;}
|
||||||
|
|
||||||
|
public UserAccount Account {
|
||||||
|
get {
|
||||||
|
|
||||||
|
return new UserAccount(){
|
||||||
|
Id = Id,
|
||||||
|
AboutMe = AboutMe,
|
||||||
|
AccountsToMail=JsonConvert.DeserializeObject<List<MailEntry>>(AccountsToMail),
|
||||||
|
Email=Email,
|
||||||
|
IsAdmin=IsAdmin,
|
||||||
|
IsInvited=IsInvited,
|
||||||
|
IsVerified=IsVerified,
|
||||||
|
PasswordHash=PasswordHash,
|
||||||
|
ProperName=ProperName,
|
||||||
|
Salt=Salt,
|
||||||
|
Username=Username,
|
||||||
|
Webhooks = JsonConvert.DeserializeObject<List<WebHook>>(Webhooks)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
set{
|
||||||
|
Id = value.Id;
|
||||||
|
AboutMe = value.AboutMe;
|
||||||
|
AccountsToMail = JsonConvert.SerializeObject(value.AccountsToMail);
|
||||||
|
Email=value.Email;
|
||||||
|
IsAdmin = value.IsAdmin;
|
||||||
|
IsInvited = value.IsInvited;
|
||||||
|
IsVerified = value.IsVerified;
|
||||||
|
PasswordHash = value.PasswordHash;
|
||||||
|
ProperName = value.ProperName;
|
||||||
|
Salt = value.Salt;
|
||||||
|
Username = value.Username;
|
||||||
|
Webhooks = JsonConvert.SerializeObject(value.Webhooks);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Dapper" Version="2.1.24" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Tesses.CMS\Tesses.CMS.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,283 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using LiteDB;
|
||||||
|
using Tesses.CMS;
|
||||||
|
namespace Tesses.CMS.Providers {
|
||||||
|
public class LiteDBContentProvider : IContentProvider
|
||||||
|
{
|
||||||
|
string dir;
|
||||||
|
ILiteDatabase db;
|
||||||
|
public LiteDBContentProvider(string dir)
|
||||||
|
{
|
||||||
|
this.dir = dir;
|
||||||
|
db=new LiteDatabase(Path.Combine(dir,"tesses-cms.db"));
|
||||||
|
}
|
||||||
|
public Movie CreateMovie(string user, string movie, string properName, string description)
|
||||||
|
{
|
||||||
|
var userId=GetUserAccount(user).Id;
|
||||||
|
|
||||||
|
Movie _movie = new Movie(){UserId = userId,Name = movie,ProperName=properName,Description = description};
|
||||||
|
_movie.Id=Movies.Insert(_movie);
|
||||||
|
return _movie;
|
||||||
|
}
|
||||||
|
public Show CreateShow(string user, string show, string properName, string description)
|
||||||
|
{
|
||||||
|
var userId=GetUserAccount(user).Id;
|
||||||
|
|
||||||
|
Show _show = new Show(){UserId = userId,Name = show,ProperName=properName,Description = description};
|
||||||
|
_show.Id=Shows.Insert(_show);
|
||||||
|
return _show;
|
||||||
|
}
|
||||||
|
private ILiteCollection<UserAccount> UserAccounts => db.GetCollection<UserAccount>("users");
|
||||||
|
private ILiteCollection<Movie> Movies => db.GetCollection<Movie>("movies");
|
||||||
|
|
||||||
|
private ILiteCollection<Show> Shows => db.GetCollection<Show>("shows");
|
||||||
|
private ILiteCollection<Season> Seasons => db.GetCollection<Season>("seasons");
|
||||||
|
private ILiteCollection<Episode> Episodes => db.GetCollection<Episode>("episodes");
|
||||||
|
|
||||||
|
private ILiteCollection<LiteDbSession> Sessions => db.GetCollection<LiteDbSession>("sessions");
|
||||||
|
private ILiteCollection<LiteDbVerificationToken> VerificationCodes => db.GetCollection<LiteDbVerificationToken>("verificationcodes");
|
||||||
|
|
||||||
|
public UserAccount GetFirstUser()
|
||||||
|
{
|
||||||
|
return GetUsers().First();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Movie GetMovie(string user, string movie)
|
||||||
|
{
|
||||||
|
var userId=GetUserAccount(user).Id;
|
||||||
|
return Movies.FindOne(e=>e.Name == movie && e.UserId == userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Show GetShow(string user,string show)
|
||||||
|
{
|
||||||
|
var userId=GetUserAccount(user).Id;
|
||||||
|
return Shows.FindOne(e=>e.Name == show && e.UserId == userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Movie> GetMovies(string user)
|
||||||
|
{
|
||||||
|
var userId = GetUserAccount(user).Id;
|
||||||
|
return GetMovies(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Movie> GetMovies(long user)
|
||||||
|
{
|
||||||
|
return Movies.Find(e=>e.UserId==user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Show> GetShows(long user)
|
||||||
|
{
|
||||||
|
return Shows.Find(e=>e.UserId==user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Show> GetShows(string user)
|
||||||
|
{
|
||||||
|
var userId = GetUserAccount(user).Id;
|
||||||
|
return GetShows(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserAccount GetUserAccount(string user)
|
||||||
|
{
|
||||||
|
return UserAccounts.FindOne(e=>e.Username == user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<UserAccount> GetUsers()
|
||||||
|
{
|
||||||
|
return UserAccounts.FindAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateUser(CMSConfiguration configuration,string user,string properName,string email,string password)
|
||||||
|
{
|
||||||
|
bool isOwner = UserAccounts.Count() == 0;
|
||||||
|
UserAccount account=new UserAccount();
|
||||||
|
account.IsAdmin=isOwner;
|
||||||
|
account.IsVerified = isOwner;
|
||||||
|
account.Username = user;
|
||||||
|
account.ProperName= properName;
|
||||||
|
account.NewSalt();
|
||||||
|
account.PasswordHash=account.GetPasswordHash(password);
|
||||||
|
account.Email = email;
|
||||||
|
UserAccounts.Insert(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateMovie(Movie movie)
|
||||||
|
{
|
||||||
|
Movies.Update(movie);
|
||||||
|
}
|
||||||
|
public void UpdateShow(Show show)
|
||||||
|
{
|
||||||
|
Shows.Update(show);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void UpdateUser(UserAccount account)
|
||||||
|
{
|
||||||
|
UserAccounts.Update(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserAccount GetUserById(long account)
|
||||||
|
{
|
||||||
|
return UserAccounts.FindById(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LiteDbSession
|
||||||
|
{
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string Session {get;set;}
|
||||||
|
|
||||||
|
public long Account {get;set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LiteDbVerificationToken
|
||||||
|
{
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string Code {get;set;}
|
||||||
|
|
||||||
|
public long Account {get;set;}
|
||||||
|
|
||||||
|
public DateTime Expires {get;set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateSession(string session, long account)
|
||||||
|
{
|
||||||
|
LiteDbSession _session=new LiteDbSession();
|
||||||
|
_session.Account = account;
|
||||||
|
_session.Session = session;
|
||||||
|
Sessions.Insert(_session);
|
||||||
|
}
|
||||||
|
public void ChangeSession(string session, long account)
|
||||||
|
{
|
||||||
|
var s=Sessions.FindOne(e=>e.Session == session);
|
||||||
|
if(s!=null){s.Account = account; Sessions.Update(s);}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteSession(string session)
|
||||||
|
{
|
||||||
|
var s=Sessions.FindOne(e=>e.Session == session);
|
||||||
|
if(s!=null){ Sessions.Delete(s.Id);}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long? GetSession(string session)
|
||||||
|
{
|
||||||
|
var s=Sessions.FindOne(e=>e.Session == session);
|
||||||
|
return s != null ? s.Account : (long?)null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsSession(string cookie)
|
||||||
|
{
|
||||||
|
return Sessions.Exists(e=>e.Session == cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateVerificationCode(string code, long account)
|
||||||
|
{
|
||||||
|
LiteDbVerificationToken liteDbVerificationToken=new LiteDbVerificationToken();
|
||||||
|
liteDbVerificationToken.Account = account;
|
||||||
|
liteDbVerificationToken.Code = code;
|
||||||
|
liteDbVerificationToken.Expires=DateTime.Now.AddDays(1);
|
||||||
|
VerificationCodes.Insert(liteDbVerificationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteVerificationCode(string code)
|
||||||
|
{
|
||||||
|
var c = VerificationCodes.FindOne(e=>e.Code==code);
|
||||||
|
if(c != null){VerificationCodes.Delete(c.Id);}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long? GetVerificationAccount(string code)
|
||||||
|
{
|
||||||
|
var c = VerificationCodes.FindOne(e=>e.Code==code);
|
||||||
|
if(c != null){
|
||||||
|
if(DateTime.Now > c.Expires)
|
||||||
|
VerificationCodes.Delete(c.Id);
|
||||||
|
else
|
||||||
|
return c.Account;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsVerificationCode(string code)
|
||||||
|
{
|
||||||
|
return VerificationCodes.Exists(e=>e.Code==code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SeasonCount(string user, string show)
|
||||||
|
{
|
||||||
|
var myShow = GetShow(user,show);
|
||||||
|
var userId = myShow.UserId;
|
||||||
|
var showId = myShow.Id;
|
||||||
|
int seasonLargest=0;
|
||||||
|
foreach(var item in Seasons.Find(e=>e.ShowId==showId && e.UserId == userId))
|
||||||
|
{
|
||||||
|
if(item.SeasonNumber > seasonLargest)
|
||||||
|
seasonLargest = item.SeasonNumber;
|
||||||
|
}
|
||||||
|
return seasonLargest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Season GetSeason(string user, string show, int season)
|
||||||
|
{
|
||||||
|
var myShow = GetShow(user,show);
|
||||||
|
var userId = myShow.UserId;
|
||||||
|
var showId = myShow.Id;
|
||||||
|
return Seasons.FindOne(e=>e.ShowId == showId && e.UserId == userId && e.SeasonNumber == season);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Season CreateSeason(string user, string show, int season, string properName, string description)
|
||||||
|
{
|
||||||
|
var myShow = GetShow(user,show);
|
||||||
|
var userId = myShow.UserId;
|
||||||
|
var showId = myShow.Id;
|
||||||
|
Season _season = new Season(){UserId = userId,ShowId = showId,ProperName=properName,Description = description, SeasonNumber=season};
|
||||||
|
_season.Id=Seasons.Insert(_season);
|
||||||
|
return _season;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int EpisodeCount(string user, string show, int season)
|
||||||
|
{
|
||||||
|
var myShow = GetShow(user,show);
|
||||||
|
var userId = myShow.UserId;
|
||||||
|
var showId = myShow.Id;
|
||||||
|
int episodeLargest=0;
|
||||||
|
foreach(var item in Episodes.Find(e=>e.ShowId==showId && e.UserId == userId && e.SeasonNumber == season))
|
||||||
|
{
|
||||||
|
if(item.EpisodeNumber > episodeLargest)
|
||||||
|
episodeLargest = item.EpisodeNumber;
|
||||||
|
}
|
||||||
|
return episodeLargest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Episode GetEpisode(string user, string show, int season, int episode)
|
||||||
|
{
|
||||||
|
var myShow = GetShow(user,show);
|
||||||
|
var userId = myShow.UserId;
|
||||||
|
var showId = myShow.Id;
|
||||||
|
return Episodes.FindOne(e=>e.ShowId == showId && e.UserId == userId && e.SeasonNumber == season && e.EpisodeNumber == episode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Episode CreateEpisode(string user, string show, int season, int episode, string episodename, string properName, string description)
|
||||||
|
{
|
||||||
|
var myShow = GetShow(user,show);
|
||||||
|
var userId = myShow.UserId;
|
||||||
|
var showId = myShow.Id;
|
||||||
|
Episode _episode = new Episode(){UserId = userId,ShowId = showId,ProperName=properName,Description = description, SeasonNumber=season, EpisodeNumber = episode,EpisodeName=episodename};
|
||||||
|
_episode.Id=Episodes.Insert(_episode);
|
||||||
|
return _episode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateEpisode(Episode episode)
|
||||||
|
{
|
||||||
|
Episodes.Update(episode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateSeason(Season season)
|
||||||
|
{
|
||||||
|
Seasons.Update(season);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LiteDb" Version="5.0.17" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Tesses.CMS\Tesses.CMS.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -1,4 +1,5 @@
|
||||||
using Tesses.WebServer;
|
using Tesses.WebServer;
|
||||||
|
using Tesses.CMS.Providers;
|
||||||
using Tesses.CMS;
|
using Tesses.CMS;
|
||||||
string path = args.Length > 0 ? args[0] : "data";
|
string path = args.Length > 0 ? args[0] : "data";
|
||||||
CMSServer server=new CMSServer(path,new LiteDBContentProvider(path));
|
CMSServer server=new CMSServer(path,new LiteDBContentProvider(path));
|
||||||
|
|
|
@ -7,14 +7,15 @@
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||||
<PackageReference Include="PlaylistsNET" Version="1.4.0" />
|
<PackageReference Include="PlaylistsNET" Version="1.4.0" />
|
||||||
<PackageReference Include="Tesses.WebServer.EasyServer" Version="1.0.1" />
|
<PackageReference Include="Tesses.WebServer.EasyServer" Version="1.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Tesses.CMS\Tesses.CMS.csproj" />
|
<ProjectReference Include="..\Tesses.CMS\Tesses.CMS.csproj" />
|
||||||
|
<ProjectReference Include="..\Tesses.CMS.Providers.LiteDb\Tesses.CMS.Providers.LiteDb.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<h1>{{propername}}</h1>
|
||||||
|
<br>
|
||||||
|
<h4>About me</h4>
|
||||||
|
<p>{{aboutme}}</p>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<form action="./account" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="exampleFormControlInput1" class="form-label">Proper Name</label>
|
||||||
|
<input type="text" class="form-control" id="exampleFormControlInput1" placeholder="Proper Name" name="proper_name" value="{{propername}}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="exampleFormControlTextarea1" class="form-label">About Me</label>
|
||||||
|
<textarea name="about_me" class="form-control" id="exampleFormControlTextarea1" rows="5">{{aboutme}}</textarea>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Change Metadata" class="btn btn-primary">
|
||||||
|
{{if notverified}}
|
||||||
|
<a class="btn btn-primary" href="./resend">Resend Verification Link</a>
|
||||||
|
{{end}}
|
||||||
|
{{if admin}}
|
||||||
|
<a class="btn btn-primary" href="./manage">Manage Accounts</a>
|
||||||
|
{{end}}
|
||||||
|
<a class="btn btn-danger" href="./logout">Logout</a>
|
||||||
|
</form>
|
|
@ -4,10 +4,10 @@
|
||||||
<label for="proper-name" class="form-label">Proper Name</label>
|
<label for="proper-name" class="form-label">Proper Name</label>
|
||||||
<input type="text" class="form-control" id="proper-name" name="proper_name" value="{{propername}}">
|
<input type="text" class="form-control" id="proper-name" name="proper_name" value="{{propername}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating">
|
<div class="mb-3">
|
||||||
<textarea class="form-control" placeholder="Description" name="description" id="floatingTextarea">{{description}}</textarea>
|
<label for="exampleFormControlTextarea1" class="form-label">Description</label>
|
||||||
<label for="floatingTextarea">Description</label>
|
<textarea name="description" class="form-control" id="exampleFormControlTextarea1" rows="5">{{description}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" value="Change Metadata" class="btn btn-primary">
|
<input type="submit" value="Change Metadata" class="btn btn-primary">
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<h1>Change season metadata</h1>
|
||||||
|
<form action="./edit" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="proper-name" class="form-label">Proper Name</label>
|
||||||
|
<input type="text" class="form-control" id="proper-name" name="proper_name" value="{{propername}}">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="exampleFormControlTextarea1" class="form-label">Description</label>
|
||||||
|
<textarea name="description" class="form-control" id="exampleFormControlTextarea1" rows="5">{{description}}</textarea>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Change Metadata" class="btn btn-primary">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<h1>Add episode</h1>
|
||||||
|
<form action="./addepisode" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Episode Name</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" aria-describedby="message">
|
||||||
|
<div id="message" class="form-text">This is the name that is used in Download Name, no spaces or /\"&,?|:;*@!#</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="number" class="form-label">Episode Number</label>
|
||||||
|
<input type="number" min="1" class="form-control" id="number" name="number" value="{{newseasonnumber}}">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="proper-name2" class="form-label">Proper Name</label>
|
||||||
|
<input type="text" class="form-control" id="proper-name2" name="proper_name" value="">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="exampleFormControlTextarea2" class="form-label">Description</label>
|
||||||
|
<textarea name="description" class="form-control" id="exampleFormControlTextarea2" rows="5"></textarea>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Add Episode" class="btn btn-primary">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<h1>Upload asset files</h1>
|
||||||
|
<form action="./upload" method="post" enctype="multipart/form-data">
|
||||||
|
<h1>You should do these in order (not required but recomended because you can only upload one at a time, that is in one tab at least)</h1>
|
||||||
|
<select class="form-select" name="type" aria-label="Select resource">
|
||||||
|
<option value="thumbnail" selected>Thumbnail (should be 120x214, the coverart for the show (also used for when episode does not have one))</option>
|
||||||
|
<option value="poster">Poster (same resoultion as show (for when episode does not have one), what you see on video player before pressing play)</option>
|
||||||
|
</select>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="formFileSm" class="form-label">File to upload</label>
|
||||||
|
<input class="form-control form-control-sm" id="formFileSm" name="file" type="file">
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Submit" class="btn btn-primary">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<a href="./extras/" target="_blank" class="btn btn-primary">View/Edit extras</a>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<h1>Change show metadata</h1>
|
||||||
|
<form action="./edit" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="proper-name" class="form-label">Proper Name</label>
|
||||||
|
<input type="text" class="form-control" id="proper-name" name="proper_name" value="{{propername}}">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="exampleFormControlTextarea1" class="form-label">Description</label>
|
||||||
|
<textarea name="description" class="form-control" id="exampleFormControlTextarea1" rows="5">{{description}}</textarea>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Change Metadata" class="btn btn-primary">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<h1>Add season</h1>
|
||||||
|
<form action="./addseason" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="number" class="form-label">Season Number</label>
|
||||||
|
<input type="number" min="1" class="form-control" id="number" name="number" value="{{newseasonnumber}}">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="proper-name2" class="form-label">Proper Name</label>
|
||||||
|
<input type="text" class="form-control" id="proper-name2" name="proper_name" value="">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="exampleFormControlTextarea2" class="form-label">Description</label>
|
||||||
|
<textarea name="description" class="form-control" id="exampleFormControlTextarea2" rows="5"></textarea>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Add Season" class="btn btn-primary">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<h1>Upload asset files</h1>
|
||||||
|
<form action="./upload" method="post" enctype="multipart/form-data">
|
||||||
|
<h1>You should do these in order (not required but recomended because you can only upload one at a time, that is in one tab at least)</h1>
|
||||||
|
<select class="form-select" name="type" aria-label="Select resource">
|
||||||
|
<option value="thumbnail" selected>Thumbnail (should be 120x214, the coverart for the show (also used for when season or episode does not have one))</option>
|
||||||
|
<option value="poster">Poster (same resoultion as show (for when season or episode does not have one), what you see on video player before pressing play)</option>
|
||||||
|
</select>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="formFileSm" class="form-label">File to upload</label>
|
||||||
|
<input class="form-control form-control-sm" id="formFileSm" name="file" type="file">
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Submit" class="btn btn-primary">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<a href="./extras/" target="_blank" class="btn btn-primary">View/Edit extras</a>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<h1 align="center"><a href="{{websiteurl}}">{{websitename}}</a></h1>
|
||||||
|
<hr>
|
||||||
|
{{body}}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<h1>Extras path: {{path}}</h1>
|
||||||
|
{{if editable}}
|
||||||
|
<h4>Create Directory</h4>
|
||||||
|
<form method="post" enctype="application/x-www-form-urlencoded" action="./mkdir">
|
||||||
|
<input type="hidden" name="parent" value="{{parent}}">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="exampleFormControlInput1" class="form-label">Directory name</label>
|
||||||
|
<input type="text" name="name" class="form-control" id="exampleFormControlInput1" placeholder="Directory name">
|
||||||
|
<input type="submit" class="btn btn-primary" value="Create Directory">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<h4>Upload File</h4>
|
||||||
|
<form method="post" enctype="multipart/form-data" action="./upload_extra">
|
||||||
|
<input type="hidden" name="parent" value="{{parent}}">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="formFileSm" class="form-label">Browse for file</label>
|
||||||
|
<input class="form-control form-control-sm" id="formFileSm" name="file" type="file">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary" value="Upload">
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
<ul class="list-group">
|
||||||
|
{{for extra in extras}}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="{{extra.path}}">{{extra.type}} {{extra.name}}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
<ul class="list-group">
|
||||||
|
{{for user in users}}
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
{{user.propername}} ({{user.name}})
|
||||||
|
<form action="./manage" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
|
<input type="hidden" name="name" value="{{user.nameattr}}">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="flexCheckVerified{{user.i}}" name="verified" {{if user.isverified}} checked {{end}}>
|
||||||
|
<label class="form-check-label" for="flexCheckVerified{{user.i}}">
|
||||||
|
Is Verified
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="flexCheckInvited{{user.i}}" name="invited" {{if user.isinvited}} checked {{end}}>
|
||||||
|
<label class="form-check-label" for="flexCheckInvited{{user.i}}">
|
||||||
|
Is Invited
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="flexCheckAdmin{{user.i}}" name="admin" {{if user.isadmin}} checked {{end}}>
|
||||||
|
<label class="form-check-label" for="flexCheckAdmin{{user.i}}">
|
||||||
|
Is Admin
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-primary" value="Save">
|
||||||
|
<a href="{{user.impersonate}}" class="btn btn-primary">Impersonate</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
|
@ -4,10 +4,18 @@
|
||||||
<h1>{{movieproper}}</h1>
|
<h1>{{movieproper}}</h1>
|
||||||
<h2>{{userproper}}</h2>
|
<h2>{{userproper}}</h2>
|
||||||
<hr>
|
<hr>
|
||||||
|
{{if moviebrowserexists}}
|
||||||
<a class="btn btn-primary" href="./play">Watch Online</a>
|
<a class="btn btn-primary" href="./play">Watch Online</a>
|
||||||
|
{{end}}
|
||||||
|
{{if movieexists}}
|
||||||
<a class="btn btn-primary" href="{{downloadurl}}" download="{{movieproperattr}}.mp4">Download</a>
|
<a class="btn btn-primary" href="{{downloadurl}}" download="{{movieproperattr}}.mp4">Download</a>
|
||||||
|
{{end}}
|
||||||
{{if editable}}
|
{{if editable}}
|
||||||
<a class="btn btn-primary" href="./edit">Edit</a>
|
<a class="btn btn-primary" href="./edit">Edit</a>
|
||||||
|
<a class="btn btn-primary" href="./subtitles">Edit Subtitles</a>
|
||||||
|
{{end}}
|
||||||
|
{{if extrasexists}}
|
||||||
|
<a class="btn btn-primary" href="./extras">Extras</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if torrentexists}}
|
{{if torrentexists}}
|
||||||
<a class="btn btn-primary" href="{{torrent}}">Torrent</a>
|
<a class="btn btn-primary" href="{{torrent}}">Torrent</a>
|
||||||
|
@ -24,6 +32,7 @@
|
||||||
Description
|
Description
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
|
<br>
|
||||||
<div id="collapseOne" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
<div id="collapseOne" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<p>{{moviedescription}}</p>
|
<p>{{moviedescription}}</p>
|
||||||
|
|
|
@ -4,8 +4,16 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{title}}</title>
|
<title>{{title}}</title>
|
||||||
<link rel="stylesheet" href="{{rooturl}}video-js.css">
|
|
||||||
<link rel="stylesheet" href="{{rooturl}}bootstrap.min.css">
|
<link rel="stylesheet" href="{{rooturl}}bootstrap.min.css">
|
||||||
|
<style>
|
||||||
|
.wrapped-link {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
.wrapped-link a {
|
||||||
|
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
@ -31,9 +39,8 @@
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{{body}}
|
{{body}}
|
||||||
</main>
|
</main>
|
||||||
<script src="{{rooturl}}bootstrap.min.js"></script>
|
<script src="{{rooturl}}bootstrap.min.js"></script>
|
||||||
<script src="{{rooturl}}video.min.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<br>
|
||||||
|
<div class="container">
|
||||||
|
<ul class="nav nav-underline" id="myTab" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" id="about-tab" data-bs-toggle="tab" data-bs-target="#about-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">About</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="episode-tab" data-bs-toggle="tab" data-bs-target="#episode-tab-pane" type="button" role="tab" aria-controls="episode-tab-pane" aria-selected="false">Episodes</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content" id="myTabContent">
|
||||||
|
<div class="tab-pane fade show active" id="about-tab-pane" role="tabpanel" aria-labelledby="about-tab" tabindex="0">
|
||||||
|
<br>
|
||||||
|
<img src="{{seasonthumbnail}}" alt="{{seasonproper}}" width="120" height="214">
|
||||||
|
<h1>{{seasonproper}}</h1>
|
||||||
|
<h2>{{showproper}}</h2>
|
||||||
|
<h2>{{userproper}}</h2>
|
||||||
|
<hr>
|
||||||
|
{{if editable}}
|
||||||
|
<a class="btn btn-primary" href="./edit">Edit</a>
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<div class="accordion" id="accordionExample">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||||
|
Description
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<br>
|
||||||
|
<div id="collapseOne" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{{seasondescription}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="episode-tab-pane" role="tabpanel" aria-labelledby="episode-tab" tabindex="0">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!--thanks to https://stackoverflow.com/a/21678797-->
|
||||||
|
<style>
|
||||||
|
ul {
|
||||||
|
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
{{for episode in episodes}}
|
||||||
|
<li>
|
||||||
|
<img src="{{episode.thumbnail}}" width="120" height="214"><br><div class="wrapped-link"><a href="./episode/{{episode.episode}}/">{{episode.proper}}</a></div>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,77 @@
|
||||||
|
<br>
|
||||||
|
<div class="container">
|
||||||
|
<ul class="nav nav-underline" id="myTab" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" id="about-tab" data-bs-toggle="tab" data-bs-target="#about-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">About</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="season-tab" data-bs-toggle="tab" data-bs-target="#season-tab-pane" type="button" role="tab" aria-controls="season-tab-pane" aria-selected="false">Seasons</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content" id="myTabContent">
|
||||||
|
<div class="tab-pane fade show active" id="about-tab-pane" role="tabpanel" aria-labelledby="about-tab" tabindex="0">
|
||||||
|
<br>
|
||||||
|
<img src="{{showthumbnail}}" alt="{{showproper}}" width="120" height="214">
|
||||||
|
<h1>{{showproper}}</h1>
|
||||||
|
<h2>{{userproper}}</h2>
|
||||||
|
<hr>
|
||||||
|
{{if editable}}
|
||||||
|
<a class="btn btn-primary" href="./edit">Edit</a>
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
{{if extrasexists}}
|
||||||
|
<a class="btn btn-primary" href="./extras">Extras</a>
|
||||||
|
{{end}}
|
||||||
|
{{if torrentexists}}
|
||||||
|
<a class="btn btn-primary" href="{{torrent}}">Torrent</a>
|
||||||
|
{{end}}
|
||||||
|
{{if torrentwextraexists}}
|
||||||
|
<a class="btn btn-primary" href="{{torrentwextra}}">Torrent With Extras</a>
|
||||||
|
{{end}}
|
||||||
|
<br>
|
||||||
|
<div class="accordion" id="accordionExample">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||||
|
Description
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<br>
|
||||||
|
<div id="collapseOne" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<p>{{showdescription}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="season-tab-pane" role="tabpanel" aria-labelledby="season-tab" tabindex="0">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!--thanks to https://stackoverflow.com/a/21678797-->
|
||||||
|
<style>
|
||||||
|
ul {
|
||||||
|
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
{{for season in seasons}}
|
||||||
|
<li>
|
||||||
|
<img src="{{season.thumbnail}}" width="120" height="214"><br><a href="./season/{{season.number}}/">{{season.proper}}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!--thanks to https://stackoverflow.com/a/21678797-->
|
||||||
|
<style>
|
||||||
|
ul {
|
||||||
|
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
{{for show in shows}}
|
||||||
|
<li>
|
||||||
|
<img src="{{show.thumbnail}}" width="120" height="214"><br><a href="../show/{{show.name}}/">{{show.proper}}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
|
@ -0,0 +1,135 @@
|
||||||
|
<div class="container text-center">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<video onloadedmetadata="set_max()" controls id="video_player" src="{{browserfile}}" width="320" height="240"></video><br>
|
||||||
|
<h3>Current Subtitle</h3>
|
||||||
|
<label for="begin" class="form-label">Start: </label><br> <input class="form-control" type="number" name="" id="begin"><button onclick="get_begin()" class="btn btn-secondary">To Player</button><button onclick="set_begin()" class="btn btn-primary">From Player</button><br>
|
||||||
|
<label for="end" class="form-label">End: </label> <br><input class="form-control" type="number" name="" id="end"><button onclick="get_end()" class="btn btn-secondary">To Player</button><button onclick="set_end()" class="btn btn-primary">From Player</button><br>
|
||||||
|
<label for="text" class="form-label">Text: </label><br> <textarea class="form-control" name="" id="text"></textarea><br>
|
||||||
|
|
||||||
|
<button onclick="add_caption()" class="btn btn-primary">Add</button><br>
|
||||||
|
<button onclick="save()" class="btn btn-secondary">Save</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<h3>All Subtitles</h3>
|
||||||
|
<ul id="captions" class="list-group"></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
captions=[];
|
||||||
|
const video_player = document.getElementById('video_player');
|
||||||
|
const begin = document.getElementById('begin');
|
||||||
|
const end = document.getElementById('end');
|
||||||
|
const text = document.getElementById('text');
|
||||||
|
const _captions = document.getElementById('captions');
|
||||||
|
function set_max()
|
||||||
|
{
|
||||||
|
begin.max = video_player.duration;
|
||||||
|
end.max = video_player.duration;
|
||||||
|
begin.value = 0;
|
||||||
|
end.value = 0;
|
||||||
|
}
|
||||||
|
function add_caption2(item)
|
||||||
|
{
|
||||||
|
const li= document.createElement('li');
|
||||||
|
li.classList.add('list-group-item');
|
||||||
|
const p=document.createElement('p');
|
||||||
|
const read = document.createElement('button');
|
||||||
|
read.innerText="To Current Subtitle";
|
||||||
|
read.classList.add('btn');
|
||||||
|
read.classList.add('btn-primary');
|
||||||
|
const write = document.createElement('button');
|
||||||
|
write.classList.add('btn');
|
||||||
|
write.classList.add('btn-secondary');
|
||||||
|
write.innerText="From Current Subtitle";
|
||||||
|
const _delete = document.createElement('button');
|
||||||
|
_delete.classList.add('btn');
|
||||||
|
_delete.classList.add('btn-danger');
|
||||||
|
_delete.onclick = ()=>{
|
||||||
|
var index=captions.indexof(item);
|
||||||
|
if(index > -1)
|
||||||
|
{
|
||||||
|
captions.splice(index,1);
|
||||||
|
}
|
||||||
|
_captions.removeChild(li);
|
||||||
|
};
|
||||||
|
_delete.innerText="Delete";
|
||||||
|
p.innerText=`${item.text.substring(0,40)} (${item.begin}=>${item.end})`;
|
||||||
|
read.onclick = ()=>{
|
||||||
|
begin.value = item.begin;
|
||||||
|
end.value = item.end;
|
||||||
|
text.value = item.text;
|
||||||
|
};
|
||||||
|
write.onclick = ()=>{
|
||||||
|
item.begin = begin.value;
|
||||||
|
item.end = end.value;
|
||||||
|
item.text = text.value;
|
||||||
|
p.innerText=`${item.text.substring(0,40)} (${item.begin}=>${item.end})`;
|
||||||
|
};
|
||||||
|
|
||||||
|
li.appendChild(p);
|
||||||
|
li.appendChild(read);
|
||||||
|
li.appendChild(write);
|
||||||
|
li.appendChild(_delete);
|
||||||
|
_captions.appendChild(li);
|
||||||
|
captions.push(item);
|
||||||
|
}
|
||||||
|
function add_caption()
|
||||||
|
{
|
||||||
|
add_caption2({
|
||||||
|
begin: begin.value,
|
||||||
|
end: end.value,
|
||||||
|
text: text.value
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
function get_end()
|
||||||
|
{
|
||||||
|
video_player.currentTime = end.value;
|
||||||
|
}
|
||||||
|
function get_begin()
|
||||||
|
{
|
||||||
|
video_player.currentTime = begin.value;
|
||||||
|
}
|
||||||
|
function set_begin()
|
||||||
|
{
|
||||||
|
begin.value = video_player.currentTime;
|
||||||
|
}
|
||||||
|
function set_end()
|
||||||
|
{
|
||||||
|
end.value = video_player.currentTime;
|
||||||
|
}
|
||||||
|
function loaded()
|
||||||
|
{
|
||||||
|
|
||||||
|
{{if hasjson}}
|
||||||
|
{{json}}.forEach(element => {
|
||||||
|
add_caption2(element);
|
||||||
|
});
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
}
|
||||||
|
addEventListener("DOMContentLoaded", (event) => {loaded();});
|
||||||
|
function save()
|
||||||
|
{
|
||||||
|
fetch("./subtitles?lang={{lang}}",{method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},body: JSON.stringify(captions)/*https://stackoverflow.com/a/29823632*/}).then(e=>{
|
||||||
|
if(e.ok)
|
||||||
|
{
|
||||||
|
alert("Saved");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alert("Failed");
|
||||||
|
}
|
||||||
|
e.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="container">
|
||||||
|
<form action="./subtitles" method="get">
|
||||||
|
<select class="form-select" name="lang" aria-label="Language">
|
||||||
|
<option selected value="">Select Language</option>
|
||||||
|
{{for lang in languages}}
|
||||||
|
<option value="{{lang.code}}">{{lang.name}}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
<input type="submit" class="btn btn-primary" value="Next">
|
||||||
|
</form>
|
||||||
|
</div>
|
|
@ -10,10 +10,10 @@
|
||||||
<label for="proper-name" class="form-label">Proper Name</label>
|
<label for="proper-name" class="form-label">Proper Name</label>
|
||||||
<input type="text" class="form-control" id="proper-name" name="proper_name">
|
<input type="text" class="form-control" id="proper-name" name="proper_name">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating">
|
<div class="mb-3">
|
||||||
<textarea class="form-control" placeholder="Description" name="description" id="floatingTextarea"></textarea>
|
<label for="exampleFormControlTextarea1" class="form-label">Description</label>
|
||||||
<label for="floatingTextarea">Description</label>
|
<textarea name="description" class="form-control" id="exampleFormControlTextarea1" rows="5"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" checked type="radio" name="type" value="movie" id="movie">
|
<input class="form-check-input" checked type="radio" name="type" value="movie" id="movie">
|
||||||
<label class="form-check-label" for="movie">
|
<label class="form-check-label" for="movie">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="container">
|
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<a href="./movies/">Movies</a>
|
<a href="./movies/">Movies</a>
|
||||||
|
@ -22,4 +22,3 @@
|
||||||
<a href="./about">About</a>
|
<a href="./about">About</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<h3>Hi {{propername}}</h3>
|
||||||
|
<p>We need to verify your email<br>to verify your account either copy or click this url: <a href="{{verifyurllink}}">{{verifyurl}}</a></p>
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Verification link sent to {{email}}</h1>
|
|
@ -23,7 +23,15 @@ overflow:hidden;}*/
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<video src="{{moviebrowserurl}}" poster="{{movieposter}}" controls>
|
<video src="{{moviebrowserurl}}" poster="{{movieposter}}" controls>
|
||||||
|
<!--langcode=langCode,name=languageName,file=subtitle.VttUrl-->
|
||||||
|
{{for lang in subtitles}}
|
||||||
|
<track
|
||||||
|
label="{{lang.name}}"
|
||||||
|
kind="subtitles"
|
||||||
|
srclang="{{lang.langcode}}"
|
||||||
|
src="{{lang.file}}"
|
||||||
|
/>
|
||||||
|
{{end}}
|
||||||
</video>
|
</video>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -5,7 +5,6 @@ using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LiteDB;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Tesses.VirtualFilesystem;
|
using Tesses.VirtualFilesystem;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Mail;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using Tesses.WebServer;
|
using Tesses.WebServer;
|
||||||
|
|
||||||
|
@ -9,6 +11,7 @@ namespace Tesses.CMS
|
||||||
{
|
{
|
||||||
public class CMSConfiguration
|
public class CMSConfiguration
|
||||||
{
|
{
|
||||||
|
public CMSEmailConfiguration Email {get;set;}
|
||||||
|
|
||||||
public ushort Port {get;set;}=62444;
|
public ushort Port {get;set;}=62444;
|
||||||
|
|
||||||
|
@ -30,6 +33,21 @@ namespace Tesses.CMS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CMSEmailConfiguration
|
||||||
|
{
|
||||||
|
public string Host {get;set;}="";
|
||||||
|
public string Email {get;set;}="";
|
||||||
|
public string User {get;set;}="";
|
||||||
|
public string Pass {get;set;}="";
|
||||||
|
|
||||||
|
public int Port {get;set;}=587;
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public MailKit.Security.SecureSocketOptions Encryption {get;set;}= MailKit.Security.SecureSocketOptions.StartTls;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class CMSNavUrl
|
public class CMSNavUrl
|
||||||
{
|
{
|
||||||
public CMSNavUrl() : this("","")
|
public CMSNavUrl() : this("","")
|
||||||
|
|
1410
Tesses.CMS/Class1.cs
1410
Tesses.CMS/Class1.cs
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Tesses.CMS
|
||||||
|
{
|
||||||
|
public class Episode
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string ProperName {get;set;}="";
|
||||||
|
|
||||||
|
public int SeasonNumber {get;set;}
|
||||||
|
|
||||||
|
public int EpisodeNumber {get;set;}
|
||||||
|
|
||||||
|
public string EpisodeName {get;set;}="";
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public long ShowId {get;set;}
|
||||||
|
[JsonIgnore]
|
||||||
|
public long UserId {get;set;}
|
||||||
|
|
||||||
|
public DateTime CreationTime {get;set;}
|
||||||
|
public DateTime LastUpdated {get;set;}
|
||||||
|
|
||||||
|
public string Description {get;set;}="";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public object Scriban(CMSConfiguration configuration,string dir,string user,string show)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
Proper = System.Web.HttpUtility.HtmlEncode( ProperName),
|
||||||
|
Season = SeasonNumber,
|
||||||
|
Episode = EpisodeNumber,
|
||||||
|
Description = System.Web.HttpUtility.HtmlEncode(Description),
|
||||||
|
Thumbnail = File.Exists(Path.Combine(dir,user,"show",show,$"Season {SeasonNumber.ToString("D2")}",$"{EpisodeName} S{SeasonNumber.ToString("D2")}E{SeasonNumber.ToString("D2")}-thumbnail.jpg")) ? $"{configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{SeasonNumber.ToString("D2")}/{EpisodeName}%20S{SeasonNumber.ToString("D2")}E{SeasonNumber.ToString("D2")}-thumbnail.jpg" : File.Exists(Path.Combine(dir,user,"show",show,$"Season {SeasonNumber.ToString("D2")}","thumbnail.jpg")) ? $"{configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{SeasonNumber.ToString("D2")}/thumbnail.jpg" : $"{configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,43 @@ namespace Tesses.CMS
|
||||||
|
|
||||||
void UpdateMovie(Movie movie);
|
void UpdateMovie(Movie movie);
|
||||||
|
|
||||||
MovieContentMetaData GetMovieContentMetaData(CMSConfiguration configuration,string user,string movie);
|
|
||||||
void CreateUser(CMSConfiguration configuration,string user,string properName,string email,string password);
|
void CreateUser(CMSConfiguration configuration,string user,string properName,string email,string password);
|
||||||
|
void UpdateUser(UserAccount account);
|
||||||
|
UserAccount GetUserById(long account);
|
||||||
|
|
||||||
|
void CreateSession(string session,long account);
|
||||||
|
void ChangeSession(string session,long account);
|
||||||
|
void DeleteSession(string session);
|
||||||
|
|
||||||
|
long? GetSession(string session);
|
||||||
|
bool ContainsSession(string cookie);
|
||||||
|
|
||||||
|
|
||||||
|
void CreateVerificationCode(string code,long account);
|
||||||
|
void DeleteVerificationCode(string code);
|
||||||
|
|
||||||
|
long? GetVerificationAccount(string code);
|
||||||
|
|
||||||
|
bool ContainsVerificationCode(string code);
|
||||||
|
IEnumerable<Show> GetShows(string user);
|
||||||
|
|
||||||
|
void UpdateShow(Show show);
|
||||||
|
void UpdateEpisode(Episode episode);
|
||||||
|
void UpdateSeason(Season season);
|
||||||
|
Show CreateShow(string user, string show, string properName, string description);
|
||||||
|
Show GetShow(string user, string show);
|
||||||
|
int SeasonCount(string user,string show);
|
||||||
|
|
||||||
|
Season GetSeason(string user,string show,int season);
|
||||||
|
|
||||||
|
Season CreateSeason(string user,string show,int season,string properName,string description);
|
||||||
|
|
||||||
|
int EpisodeCount(string user,string show,int season);
|
||||||
|
|
||||||
|
Episode GetEpisode(string user,string show,int season,int episode);
|
||||||
|
|
||||||
|
Episode CreateEpisode(string user,string show,int season,int episode,string episodename,string properName,string description);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MovieContentMetaData
|
public class MovieContentMetaData
|
||||||
|
@ -67,8 +102,11 @@ namespace Tesses.CMS
|
||||||
[JsonProperty("language_code")]
|
[JsonProperty("language_code")]
|
||||||
public string LanguageCode {get;set;}="";
|
public string LanguageCode {get;set;}="";
|
||||||
|
|
||||||
[JsonProperty("url")]
|
[JsonProperty("srt_url")]
|
||||||
|
|
||||||
public string SubtitleUrl {get;set;}
|
public string SrtUrl {get;set;}="";
|
||||||
|
|
||||||
|
[JsonProperty("vtt_url")]
|
||||||
|
public string VttUrl {get;set;}="";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,132 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using LiteDB;
|
|
||||||
|
|
||||||
namespace Tesses.CMS {
|
|
||||||
public class LiteDBContentProvider : IContentProvider
|
|
||||||
{
|
|
||||||
string dir;
|
|
||||||
ILiteDatabase db;
|
|
||||||
public LiteDBContentProvider(string dir)
|
|
||||||
{
|
|
||||||
this.dir = dir;
|
|
||||||
db=new LiteDatabase(Path.Combine(dir,"tesses-cms.db"));
|
|
||||||
}
|
|
||||||
public Movie CreateMovie(string user, string movie, string properName, string description)
|
|
||||||
{
|
|
||||||
var userId=GetUserAccount(user).Id;
|
|
||||||
|
|
||||||
Movie _movie = new Movie(){UserId = userId,Name = movie,ProperName=properName,Description = description};
|
|
||||||
_movie.Id=Movies.Insert(_movie);
|
|
||||||
return _movie;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ILiteCollection<UserAccount> UserAccounts => db.GetCollection<UserAccount>("users");
|
|
||||||
private ILiteCollection<Movie> Movies => db.GetCollection<Movie>("movies");
|
|
||||||
|
|
||||||
public UserAccount GetFirstUser()
|
|
||||||
{
|
|
||||||
return GetUsers().First();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Movie GetMovie(string user, string movie)
|
|
||||||
{
|
|
||||||
var userId=GetUserAccount(user).Id;
|
|
||||||
return Movies.FindOne(e=>e.Name == movie && e.UserId == userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MovieContentMetaData GetMovieContentMetaData(CMSConfiguration configuration, string user, string movie)
|
|
||||||
{
|
|
||||||
var _movie= new MovieContentMetaData(){
|
|
||||||
PosterUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/poster.jpg",
|
|
||||||
ThumbnailUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/thumbnail.jpg",
|
|
||||||
BrowserStream = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/browser.mp4",
|
|
||||||
DownloadStream = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.mp4",
|
|
||||||
MovieTorrentUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.torrent",
|
|
||||||
MovieWithExtrasTorrentUrl=$"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}_withextras.torrent"
|
|
||||||
};
|
|
||||||
string subDir=Path.Combine(dir,"content", user, "movie",movie,"subtitles");
|
|
||||||
if(Directory.Exists(subDir))
|
|
||||||
foreach(var language in Directory.GetDirectories(subDir))
|
|
||||||
{
|
|
||||||
string languageCode=Path.GetFileName(language); //en-US for english
|
|
||||||
_movie.SubtitlesStreams.Add(new SubtitleStream(){LanguageCode=languageCode, SubtitleUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/subtitles/{languageCode}/{movie}.srt"});
|
|
||||||
}
|
|
||||||
string extrasPath = Path.Combine(dir,"content", 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Movie> GetMovies(string user)
|
|
||||||
{
|
|
||||||
var userId = GetUserAccount(user).Id;
|
|
||||||
return GetMovies(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Movie> GetMovies(long user)
|
|
||||||
{
|
|
||||||
return Movies.Find(e=>e.UserId==user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserAccount GetUserAccount(string user)
|
|
||||||
{
|
|
||||||
return UserAccounts.FindOne(e=>e.Username == user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<UserAccount> GetUsers()
|
|
||||||
{
|
|
||||||
return UserAccounts.FindAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateUser(CMSConfiguration configuration,string user,string properName,string email,string password)
|
|
||||||
{
|
|
||||||
bool isOwner = UserAccounts.Count() == 0;
|
|
||||||
UserAccount account=new UserAccount();
|
|
||||||
account.IsAdmin=isOwner;
|
|
||||||
account.IsVerified = isOwner;
|
|
||||||
account.Username = user;
|
|
||||||
account.ProperName= properName;
|
|
||||||
account.NewSalt();
|
|
||||||
account.PasswordHash=account.GetPasswordHash(password);
|
|
||||||
account.Email = email;
|
|
||||||
UserAccounts.Insert(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMovie(Movie movie)
|
|
||||||
{
|
|
||||||
Movies.Update(movie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Tesses.CMS
|
|
||||||
{
|
|
||||||
public class MockProvider : IContentProvider
|
|
||||||
{
|
|
||||||
List<Movie> movies=new List<Movie>();
|
|
||||||
UserAccount account2=new UserAccount();
|
|
||||||
public MockProvider()
|
|
||||||
{
|
|
||||||
Movie movie=new Movie(){Id=1,ProperName="Big Buck Bunny",Name="BigBuckBunny",UserId=1,CreationTime=new System.DateTime(2018,7,24,14,0,0), Description="Blenders Big Buck Bunny" };
|
|
||||||
movie.LastUpdated = movie.CreationTime;
|
|
||||||
movies.Add(movie);
|
|
||||||
account2.IsAdmin=true;
|
|
||||||
account2.Email = "johndoe@example.com";
|
|
||||||
account2.ProperName = "John Doe";
|
|
||||||
account2.Salt = "Jieaifnaienaifeniaienaif";
|
|
||||||
account2.PasswordHash = "0v4LGsBWyB6LMEuFDzU+H9Iy4Z5T4nyt96FBJ+YXHz0=";
|
|
||||||
account2.Username = "johndoe";
|
|
||||||
account2.Id = 2;
|
|
||||||
UserAccount account1=new UserAccount();
|
|
||||||
account1.Email="blender@example.com";
|
|
||||||
account1.PasswordHash="";
|
|
||||||
account1.ProperName = "Blender Studios";
|
|
||||||
account1.Username = "Blender";
|
|
||||||
account1.Salt ="";
|
|
||||||
account1.Id = 1;
|
|
||||||
|
|
||||||
Users.Add(account1);
|
|
||||||
|
|
||||||
Users.Add(account2);
|
|
||||||
}
|
|
||||||
List<UserAccount> Users =new List<UserAccount>();
|
|
||||||
public Movie CreateMovie(string user, string movie,string properName, string description)
|
|
||||||
{
|
|
||||||
Movie _movie = new Movie();
|
|
||||||
_movie.ProperName = properName;
|
|
||||||
_movie.Name = movie;
|
|
||||||
_movie.Id = movies.Count + 1;
|
|
||||||
_movie.UserId = 2;
|
|
||||||
_movie.Description = description;
|
|
||||||
_movie.CreationTime = DateTime.Now;
|
|
||||||
_movie.LastUpdated = _movie.CreationTime;
|
|
||||||
movies.Add(_movie);
|
|
||||||
return _movie;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Movie GetMovie(string user, string movie)
|
|
||||||
{
|
|
||||||
if(user == "Blender" && movie=="BigBuckBunny")
|
|
||||||
{
|
|
||||||
return movies[0];
|
|
||||||
}
|
|
||||||
foreach(var item in movies)
|
|
||||||
{
|
|
||||||
if(item.Name == movie)
|
|
||||||
{
|
|
||||||
if(Users[(int)(item.UserId-1)].Username == user)
|
|
||||||
{
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public UserAccount GetUserAccount(string user)
|
|
||||||
{
|
|
||||||
foreach(var _user in Users)
|
|
||||||
{
|
|
||||||
if(_user.Username == user) return _user;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public IEnumerable<Movie> GetMovies(string user)
|
|
||||||
{
|
|
||||||
foreach(var movie in movies)
|
|
||||||
{
|
|
||||||
if(Users[(int)(movie.UserId-1)].Username == user)
|
|
||||||
{
|
|
||||||
yield return movie;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<UserAccount> GetUsers()
|
|
||||||
{
|
|
||||||
return Users;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMovie(Movie movie)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserAccount GetFirstUser()
|
|
||||||
{
|
|
||||||
return Users[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Movie> GetMovies(long user)
|
|
||||||
{
|
|
||||||
foreach(var movie in movies)
|
|
||||||
{
|
|
||||||
if(movie.UserId == user)
|
|
||||||
{
|
|
||||||
yield return movie;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public MovieContentMetaData GetMovieContentMetaData(CMSConfiguration configuration, string user, string movie)
|
|
||||||
{
|
|
||||||
MovieContentMetaData metaData=new MovieContentMetaData();
|
|
||||||
metaData.DownloadStream = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.mp4";
|
|
||||||
metaData.ThumbnailUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/thumbnail.jpg";;
|
|
||||||
metaData.PosterUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/poster.jpg";
|
|
||||||
metaData.BrowserStream = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/browser.mp4";
|
|
||||||
metaData.MovieTorrentUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}.torrent";
|
|
||||||
metaData.MovieWithExtrasTorrentUrl = $"{configuration.Root.TrimEnd('/')}/content/{user}/movie/{movie}/{movie}_withextras.torrent";
|
|
||||||
|
|
||||||
if(user == "Blender" && movie == "BigBuckBunny")
|
|
||||||
{
|
|
||||||
metaData.ThumbnailUrl = "https://imgs.search.brave.com/3euIrrvtlxdwORlondpP-QTiWhC1_ARU5VlvgmkEnao/rs:fit:500:0:0/g:ce/aHR0cHM6Ly9tLm1l/ZGlhLWFtYXpvbi5j/b20vaW1hZ2VzL00v/TVY1Qk5qUmpZalJo/Tm1RdE5XRTBZUzAw/TldJd0xXRmhZalV0/TVRrelpUVXdZVEU0/TVRCaVhrRXlYa0Zx/Y0dkZVFYVnlOakEz/T1RJNU1qQUAuanBn";
|
|
||||||
metaData.BrowserStream= "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4";
|
|
||||||
metaData.DownloadStream = "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4";
|
|
||||||
metaData.PosterUrl = "https://imgs.search.brave.com/3euIrrvtlxdwORlondpP-QTiWhC1_ARU5VlvgmkEnao/rs:fit:500:0:0/g:ce/aHR0cHM6Ly9tLm1l/ZGlhLWFtYXpvbi5j/b20vaW1hZ2VzL00v/TVY1Qk5qUmpZalJo/Tm1RdE5XRTBZUzAw/TldJd0xXRmhZalV0/TVRrelpUVXdZVEU0/TVRCaVhrRXlYa0Zx/Y0dkZVFYVnlOakEz/T1RJNU1qQUAuanBn";
|
|
||||||
}
|
|
||||||
return metaData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateUser(CMSConfiguration configuration, string user,string properName, string email, string password)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Tesses.CMS
|
||||||
|
{
|
||||||
|
public class Season
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string ProperName {get;set;}="";
|
||||||
|
|
||||||
|
public int SeasonNumber {get;set;}
|
||||||
|
[JsonIgnore]
|
||||||
|
public long ShowId {get;set;}
|
||||||
|
[JsonIgnore]
|
||||||
|
public long UserId {get;set;}
|
||||||
|
|
||||||
|
public DateTime CreationTime {get;set;}
|
||||||
|
public DateTime LastUpdated {get;set;}
|
||||||
|
|
||||||
|
public string Description {get;set;}="";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public object Scriban(CMSConfiguration configuration,string dir,string user,string show)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
Proper = System.Web.HttpUtility.HtmlEncode( ProperName),
|
||||||
|
Number = SeasonNumber,
|
||||||
|
Description = System.Web.HttpUtility.HtmlEncode(Description),
|
||||||
|
Thumbnail = File.Exists(Path.Combine(dir,user,"show",show,$"Season {SeasonNumber.ToString("D2")}","thumbnail.jpg")) ? $"{configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/Season%20{SeasonNumber.ToString("D2")}/thumbnail.jpg" : $"{configuration.Root.TrimEnd('/')}/content/{user}/show/{show}/thumbnail.jpg"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Tesses.CMS
|
||||||
|
{
|
||||||
|
public class Show
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string ProperName {get;set;}="";
|
||||||
|
|
||||||
|
public string Name {get;set;}="";
|
||||||
|
[JsonIgnore]
|
||||||
|
public long UserId {get;set;}
|
||||||
|
|
||||||
|
public DateTime CreationTime {get;set;}
|
||||||
|
public DateTime LastUpdated {get;set;}
|
||||||
|
|
||||||
|
public string Description {get;set;}="";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public object Scriban(CMSConfiguration configuration,string user)
|
||||||
|
{
|
||||||
|
return new {
|
||||||
|
Proper = System.Web.HttpUtility.HtmlEncode( ProperName),
|
||||||
|
Name = System.Web.HttpUtility.HtmlEncode(Name),
|
||||||
|
Description = System.Web.HttpUtility.HtmlEncode(Description),
|
||||||
|
Thumbnail = $"{configuration.Root.TrimEnd('/')}/content/{user}/show/{Name}/thumbnail.jpg"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,12 +9,12 @@
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LiteDb" Version="5.0.17" />
|
<PackageReference Include="MailKit" Version="4.3.0" />
|
||||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||||
<PackageReference Include="PlaylistsNET" Version="1.4.0" />
|
<PackageReference Include="PlaylistsNET" Version="1.4.0" />
|
||||||
<PackageReference Include="Scriban" Version="5.9.0" />
|
<PackageReference Include="Scriban" Version="5.9.0" />
|
||||||
<PackageReference Include="Tesses.WebServer" Version="1.0.4.1" />
|
<PackageReference Include="Tesses.WebServer" Version="1.0.4.1" />
|
||||||
<PackageReference Include="Tesses.WebServer.Swagme" Version="1.0.2" />
|
<PackageReference Include="Tesses.WebServer.Swagme" Version="1.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -69,6 +69,7 @@ namespace Tesses.CMS
|
||||||
{
|
{
|
||||||
public long UserId {get;set;}
|
public long UserId {get;set;}
|
||||||
|
|
||||||
|
public bool EnableUpdates {get;set;}
|
||||||
public bool EnableMovies {get;set;}
|
public bool EnableMovies {get;set;}
|
||||||
|
|
||||||
public bool EnabledAlbums {get;set;}
|
public bool EnabledAlbums {get;set;}
|
||||||
|
|
Loading…
Reference in New Issue