Cleaning up

This commit is contained in:
Zoe Roux 2020-02-29 16:32:27 +01:00
parent 8c8db5e9b6
commit 253b8561bc
42 changed files with 1774 additions and 1804 deletions

View File

@ -3,8 +3,8 @@ using System.Threading.Tasks;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public interface ICrawler public interface ICrawler
{ {
Task StartAsync(CancellationToken cancellationToken); Task StartAsync(CancellationToken cancellationToken);
} }
} }

View File

@ -4,63 +4,63 @@ using System.Collections.Generic;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public interface ILibraryManager public interface ILibraryManager
{ {
//Read values //Read values
string GetShowExternalIDs(long showID); string GetShowExternalIDs(long showID);
Studio GetStudio(long showID); Studio GetStudio(long showID);
IEnumerable<PeopleLink> GetPeople(long showID); IEnumerable<PeopleLink> GetPeople(long showID);
IEnumerable<Genre> GetGenreForShow(long showID); IEnumerable<Genre> GetGenreForShow(long showID);
IEnumerable<Season> GetSeasons(long showID); IEnumerable<Season> GetSeasons(long showID);
int GetSeasonCount(string showSlug, long seasonNumber); int GetSeasonCount(string showSlug, long seasonNumber);
IEnumerable<Show> GetShowsInCollection(long collectionID); IEnumerable<Show> GetShowsInCollection(long collectionID);
IEnumerable<Show> GetShowsInLibrary(long libraryID); IEnumerable<Show> GetShowsInLibrary(long libraryID);
IEnumerable<Show> GetShowsByPeople(string peopleSlug); IEnumerable<Show> GetShowsByPeople(string peopleSlug);
IEnumerable<string> GetLibrariesPath(); IEnumerable<string> GetLibrariesPath();
//Internal read //Internal read
(Track video, IEnumerable<Track> audios, IEnumerable<Track> subtitles) GetStreams(long episodeID, string showSlug); (Track video, IEnumerable<Track> audios, IEnumerable<Track> subtitles) GetStreams(long episodeID, string showSlug);
Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, bool forced); Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, bool forced);
//Public read //Public read
IEnumerable<Show> GetShows(); IEnumerable<Show> GetShows();
IEnumerable<Show> GetShows(string searchQuery); IEnumerable<Show> GetShows(string searchQuery);
Library GetLibrary(string librarySlug); Library GetLibrary(string librarySlug);
IEnumerable<Library> GetLibraries(); IEnumerable<Library> GetLibraries();
Show GetShowBySlug(string slug); Show GetShowBySlug(string slug);
Show GetShow(string path); Show GetShow(string path);
Season GetSeason(string showSlug, long seasonNumber); Season GetSeason(string showSlug, long seasonNumber);
IEnumerable<Episode> GetEpisodes(string showSlug); IEnumerable<Episode> GetEpisodes(string showSlug);
IEnumerable<Episode> GetEpisodes(string showSlug, long seasonNumber); IEnumerable<Episode> GetEpisodes(string showSlug, long seasonNumber);
Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber); Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber);
WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber, bool complete = true); WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber, bool complete = true);
People GetPeopleBySlug(string slug); People GetPeopleBySlug(string slug);
Genre GetGenreBySlug(string slug); Genre GetGenreBySlug(string slug);
Studio GetStudioBySlug(string slug); Studio GetStudioBySlug(string slug);
Collection GetCollection(string slug); Collection GetCollection(string slug);
IEnumerable<Episode> GetAllEpisodes(); IEnumerable<Episode> GetAllEpisodes();
IEnumerable<Episode> SearchEpisodes(string searchQuery); IEnumerable<Episode> SearchEpisodes(string searchQuery);
IEnumerable<People> SearchPeople(string searchQuery); IEnumerable<People> SearchPeople(string searchQuery);
IEnumerable<Genre> SearchGenres(string searchQuery); IEnumerable<Genre> SearchGenres(string searchQuery);
IEnumerable<Studio> SearchStudios(string searchQuery); IEnumerable<Studio> SearchStudios(string searchQuery);
//Check if value exists //Check if value exists
bool IsCollectionRegistered(string collectionSlug, out long collectionID); bool IsCollectionRegistered(string collectionSlug, out long collectionID);
bool IsShowRegistered(string showPath, out long showID); bool IsShowRegistered(string showPath, out long showID);
bool IsSeasonRegistered(long showID, long seasonNumber, out long seasonID); bool IsSeasonRegistered(long showID, long seasonNumber, out long seasonID);
bool IsEpisodeRegistered(string episodePath, out long episodeID); bool IsEpisodeRegistered(string episodePath, out long episodeID);
//Register values //Register values
long RegisterCollection(Collection collection); long RegisterCollection(Collection collection);
long RegisterShow(Show show); long RegisterShow(Show show);
long RegisterSeason(Season season); long RegisterSeason(Season season);
long RegisterEpisode(Episode episode); long RegisterEpisode(Episode episode);
long RegisterTrack(Track track); long RegisterTrack(Track track);
void RegisterShowLinks(Library library, Collection collection, Show show); void RegisterShowLinks(Library library, Collection collection, Show show);
void RemoveShow(long showID); void RemoveShow(long showID);
void RemoveSeason(long seasonID); void RemoveSeason(long seasonID);
void RemoveEpisode(long episodeID); void RemoveEpisode(long episodeID);
void ClearSubtitles(long episodeID); void ClearSubtitles(long episodeID);
} }
} }

View File

@ -4,22 +4,22 @@ using System.Threading.Tasks;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public interface IMetadataProvider public interface IMetadataProvider
{ {
public string Name { get; } public string Name { get; }
//For the collection //For the collection
Task<Collection> GetCollectionFromName(string name); Task<Collection> GetCollectionFromName(string name);
//For the show //For the show
Task<Show> GetShowByID(Show show); Task<Show> GetShowByID(Show show);
Task<Show> GetShowFromName(string showName, bool isMovie); Task<Show> GetShowFromName(string showName, bool isMovie);
Task<IEnumerable<PeopleLink>> GetPeople(Show show); Task<IEnumerable<PeopleLink>> GetPeople(Show show);
//For the seasons //For the seasons
Task<Season> GetSeason(Show show, long seasonNumber); Task<Season> GetSeason(Show show, long seasonNumber);
//For the episodes //For the episodes
Task<Episode> GetEpisode(Show show, long seasonNumber, long episodeNumber, long absoluteNumber); Task<Episode> GetEpisode(Show show, long seasonNumber, long episodeNumber, long absoluteNumber);
} }
} }

View File

@ -4,10 +4,10 @@ using System.Threading.Tasks;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public interface IThumbnailsManager public interface IThumbnailsManager
{ {
Task<Show> Validate(Show show); Task<Show> Validate(Show show);
Task<IEnumerable<PeopleLink>> Validate(List<PeopleLink> actors); Task<IEnumerable<PeopleLink>> Validate(List<PeopleLink> actors);
Task<Episode> Validate(Episode episode); Task<Episode> Validate(Episode episode);
} }
} }

View File

@ -4,18 +4,18 @@ using System.Threading.Tasks;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public interface ITranscoder public interface ITranscoder
{ {
// Should transcode to a mp4 container (same video/audio format if possible, no subtitles). // Should transcode to a mp4 container (same video/audio format if possible, no subtitles).
Task<string> Transmux(WatchItem episode); Task<string> Transmux(WatchItem episode);
// Should transcode to a mp4 container with a h264 video format and a AAC audio format, no subtitles. // Should transcode to a mp4 container with a h264 video format and a AAC audio format, no subtitles.
Task<string> Transcode(WatchItem episode); Task<string> Transcode(WatchItem episode);
// Get video and audio tracks infos (codec, name, language...) // Get video and audio tracks infos (codec, name, language...)
Task<Track[]> GetTrackInfo(string path); Task<Track[]> GetTrackInfo(string path);
// Extract all subtitles of a video and save them in the subtitles sub-folder. // Extract all subtitles of a video and save them in the subtitles sub-folder.
Task<Track[]> ExtractSubtitles(string path); Task<Track[]> ExtractSubtitles(string path);
} }
} }

View File

@ -1,21 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Title>Kyoo.Common</Title> <Title>Kyoo.Common</Title>
<Authors>Anonymus Raccoon</Authors> <Authors>Anonymus Raccoon</Authors>
<Description>Base package to create plugins for Kyoo.</Description> <Description>Base package to create plugins for Kyoo.</Description>
<PackageProjectUrl>https://github.com/AnonymusRaccoon/Kyoo</PackageProjectUrl> <PackageProjectUrl>https://github.com/AnonymusRaccoon/Kyoo</PackageProjectUrl>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl> <RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<Company>SDG</Company> <Company>SDG</Company>
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression> <PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageVersion>1.0.11</PackageVersion> <PackageVersion>1.0.11</PackageVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -4,55 +4,55 @@ using System.Linq;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Collection : IMergable<Collection> public class Collection : IMergable<Collection>
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Poster { get; set; } public string Poster { get; set; }
public string Overview { get; set; } public string Overview { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; } [JsonIgnore] public string ImgPrimary { get; set; }
public IEnumerable<Show> Shows; public IEnumerable<Show> Shows;
public Collection() { } public Collection() { }
public Collection(string slug, string name, string overview, string imgPrimary) public Collection(string slug, string name, string overview, string imgPrimary)
{ {
Slug = slug; Slug = slug;
Name = name; Name = name;
Overview = overview; Overview = overview;
ImgPrimary = imgPrimary; ImgPrimary = imgPrimary;
}
public Show AsShow()
{
return new Show(Slug, Name, null, null, Overview, null, null, null, null, null, null)
{
IsCollection = true
};
}
public Collection Merge(Collection collection)
{
if (collection == null)
return this;
if (ID == -1)
ID = collection.ID;
if (Slug == null)
Slug = collection.Slug;
if (Name == null)
Name = collection.Name;
if (Poster == null)
Poster = collection.Poster;
if (Overview == null)
Overview = collection.Overview;
if (ImgPrimary == null)
ImgPrimary = collection.ImgPrimary;
if (Shows == null)
Shows = collection.Shows;
else
Shows = Shows.Concat(collection.Shows);
return this;
} }
}
public Show AsShow()
{
return new Show(Slug, Name, null, null, Overview, null, null, null, null, null, null)
{
IsCollection = true
};
}
public Collection Merge(Collection collection)
{
if (collection == null)
return this;
if (ID == -1)
ID = collection.ID;
if (Slug == null)
Slug = collection.Slug;
if (Name == null)
Name = collection.Name;
if (Poster == null)
Poster = collection.Poster;
if (Overview == null)
Overview = collection.Overview;
if (ImgPrimary == null)
ImgPrimary = collection.ImgPrimary;
if (Shows == null)
Shows = collection.Shows;
else
Shows = Shows.Concat(collection.Shows);
return this;
}
}
} }

View File

@ -4,112 +4,107 @@ using System.Collections.Generic;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Episode : IMergable<Episode> public class Episode : IMergable<Episode>
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long ShowID { get; set; } [JsonIgnore] public long ShowID { get; set; }
[JsonIgnore] public virtual Show Show { get; set; } [JsonIgnore] public virtual Show Show { get; set; }
[JsonIgnore] public long SeasonID { get; set; } [JsonIgnore] public long SeasonID { get; set; }
[JsonIgnore] public virtual Season Season { get; set; } [JsonIgnore] public virtual Season Season { get; set; }
public long SeasonNumber { get; set; } public long SeasonNumber { get; set; }
public long EpisodeNumber { get; set; } public long EpisodeNumber { get; set; }
public long AbsoluteNumber { get; set; } public long AbsoluteNumber { get; set; }
[JsonIgnore] public string Path { get; set; } [JsonIgnore] public string Path { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Overview { get; set; } public string Overview { get; set; }
public DateTime? ReleaseDate { get; set; } public DateTime? ReleaseDate { get; set; }
public long Runtime { get; set; } //This runtime variable should be in minutes public long Runtime { get; set; } //This runtime variable should be in minutes
[JsonIgnore] public string ImgPrimary { get; set; } [JsonIgnore] public string ImgPrimary { get; set; }
public string ExternalIDs { get; set; } public string ExternalIDs { get; set; }
[JsonIgnore] public virtual IEnumerable<Track> Tracks { get; set; } [JsonIgnore] public virtual IEnumerable<Track> Tracks { get; set; }
public string ShowTitle; //Used in the API response only public string ShowTitle; //Used in the API response only
public string Link; //Used in the API response only public string Link; //Used in the API response only
public string Thumb; //Used in the API response only public string Thumb; //Used in the API response only
public Episode() public Episode()
{ {
SeasonNumber = -1; SeasonNumber = -1;
EpisodeNumber = -1; EpisodeNumber = -1;
AbsoluteNumber = -1; AbsoluteNumber = -1;
} }
public Episode(long seasonNumber, long episodeNumber, long absoluteNumber, string title, string overview, DateTime? releaseDate, long runtime, string imgPrimary, string externalIDs) public Episode(long seasonNumber, long episodeNumber, long absoluteNumber, string title, string overview, DateTime? releaseDate, long runtime, string imgPrimary, string externalIDs)
{ {
SeasonNumber = seasonNumber; SeasonNumber = seasonNumber;
EpisodeNumber = episodeNumber; EpisodeNumber = episodeNumber;
AbsoluteNumber = absoluteNumber; AbsoluteNumber = absoluteNumber;
Title = title; Title = title;
Overview = overview; Overview = overview;
ReleaseDate = releaseDate; ReleaseDate = releaseDate;
Runtime = runtime; Runtime = runtime;
ImgPrimary = imgPrimary; ImgPrimary = imgPrimary;
ExternalIDs = externalIDs; ExternalIDs = externalIDs;
} }
public Episode(long showID, long seasonID, long seasonNumber, long episodeNumber, long absoluteNumber, string path, string title, string overview, DateTime? releaseDate, long runtime, string imgPrimary, string externalIDs) public Episode(long showID, long seasonID, long seasonNumber, long episodeNumber, long absoluteNumber, string path, string title, string overview, DateTime? releaseDate, long runtime, string imgPrimary, string externalIDs)
{ {
ShowID = showID; ShowID = showID;
SeasonID = seasonID; SeasonID = seasonID;
SeasonNumber = seasonNumber; SeasonNumber = seasonNumber;
EpisodeNumber = episodeNumber; EpisodeNumber = episodeNumber;
AbsoluteNumber = absoluteNumber; AbsoluteNumber = absoluteNumber;
Path = path; Path = path;
Title = title; Title = title;
Overview = overview; Overview = overview;
ReleaseDate = releaseDate; ReleaseDate = releaseDate;
Runtime = runtime; Runtime = runtime;
ImgPrimary = imgPrimary; ImgPrimary = imgPrimary;
ExternalIDs = externalIDs; ExternalIDs = externalIDs;
} }
public static string GetSlug(string showSlug, long seasonNumber, long episodeNumber) public static string GetSlug(string showSlug, long seasonNumber, long episodeNumber)
{ {
return showSlug + "-s" + seasonNumber + "e" + episodeNumber; return showSlug + "-s" + seasonNumber + "e" + episodeNumber;
} }
public Episode SetLink(string showSlug) public Episode SetLink(string showSlug)
{ {
Link = GetSlug(showSlug, SeasonNumber, EpisodeNumber); Link = GetSlug(showSlug, SeasonNumber, EpisodeNumber);
Thumb = "thumb/" + Link; Thumb = "thumb/" + Link;
return this; return this;
} }
public Episode Merge(Episode other) public Episode Merge(Episode other)
{ {
if (other == null) if (other == null)
return this; return this;
if (ID == -1) if (ID == -1)
ID = other.ID; ID = other.ID;
if (ShowID == -1) if (ShowID == -1)
ShowID = other.ShowID; ShowID = other.ShowID;
if (SeasonID == -1) if (SeasonID == -1)
SeasonID = other.SeasonID; SeasonID = other.SeasonID;
if (SeasonNumber == -1) if (SeasonNumber == -1)
SeasonNumber = other.SeasonNumber; SeasonNumber = other.SeasonNumber;
if (EpisodeNumber == -1) if (EpisodeNumber == -1)
EpisodeNumber = other.EpisodeNumber; EpisodeNumber = other.EpisodeNumber;
if (AbsoluteNumber == -1) if (AbsoluteNumber == -1)
AbsoluteNumber = other.AbsoluteNumber; AbsoluteNumber = other.AbsoluteNumber;
if (Path == null) Path ??= other.Path;
Path = other.Path; Title ??= other.Title;
if (Title == null) Overview ??= other.Overview;
Title = other.Title; ReleaseDate ??= other.ReleaseDate;
if (Overview == null) if (Runtime == -1)
Overview = other.Overview; Runtime = other.Runtime;
if (ReleaseDate == null) ImgPrimary ??= other.ImgPrimary;
ReleaseDate = other.ReleaseDate; ExternalIDs += '|' + other.ExternalIDs;
if (Runtime == -1) return this;
Runtime = other.Runtime; }
if (ImgPrimary == null) }
ImgPrimary = other.ImgPrimary;
ExternalIDs += '|' + other.ExternalIDs;
return this;
}
}
} }

View File

@ -3,25 +3,25 @@ using Newtonsoft.Json;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Genre public class Genre
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Name { get; set; } public string Name { get; set; }
// public IEnumerable<Show> Shows { get; set; } // public IEnumerable<Show> Shows { get; set; }
public Genre(string slug, string name) public Genre(string slug, string name)
{ {
Slug = slug; Slug = slug;
Name = name; Name = name;
} }
public Genre(long id, string slug, string name) public Genre(long id, string slug, string name)
{ {
ID = id; ID = id;
Slug = slug; Slug = slug;
Name = name; Name = name;
} }
} }
} }

View File

@ -2,22 +2,22 @@
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Library public class Library
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string[] Paths { get; set; } public string[] Paths { get; set; }
public string[] Providers { get; set; } public string[] Providers { get; set; }
public Library() { } public Library() { }
public Library(string slug, string name, string[] paths, string[] providers) public Library(string slug, string name, string[] paths, string[] providers)
{ {
Slug = slug; Slug = slug;
Name = name; Name = name;
Paths = paths; Paths = paths;
Providers = providers; Providers = providers;
} }
} }
} }

View File

@ -3,37 +3,34 @@ using Newtonsoft.Json;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class People : IMergable<People> public class People : IMergable<People>
{ {
public string Slug { get; set; } public string Slug { get; set; }
public string Name { get; set; } public string Name { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; } [JsonIgnore] public string ImgPrimary { get; set; }
public string ExternalIDs { get; set; } public string ExternalIDs { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleLink> Roles { get; set; } [JsonIgnore] public virtual IEnumerable<PeopleLink> Roles { get; set; }
public People() {} public People() {}
public People(string slug, string name, string imgPrimary, string externalIDs) public People(string slug, string name, string imgPrimary, string externalIDs)
{ {
Slug = slug; Slug = slug;
Name = name; Name = name;
ImgPrimary = imgPrimary; ImgPrimary = imgPrimary;
ExternalIDs = externalIDs; ExternalIDs = externalIDs;
} }
public People Merge(People other) public People Merge(People other)
{ {
if (other == null) if (other == null)
return this; return this;
if (Slug == null) Slug ??= other.Slug;
Slug = other.Slug; Name ??= other.Name;
if (Name == null) ImgPrimary ??= other.ImgPrimary;
Name = other.Name; ExternalIDs += '|' + other.ExternalIDs;
if (ImgPrimary == null) return this;
ImgPrimary = other.ImgPrimary; }
ExternalIDs += '|' + other.ExternalIDs; }
return this;
}
}
} }

View File

@ -2,36 +2,36 @@ using Newtonsoft.Json;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class PeopleLink public class PeopleLink
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
[JsonIgnore] public string PeopleID { get; set; } [JsonIgnore] public string PeopleID { get; set; }
[JsonIgnore] public virtual People People { get; set; } [JsonIgnore] public virtual People People { get; set; }
public string Slug => People.Slug; public string Slug => People.Slug;
public string Name => People.Name; public string Name => People.Name;
public string ExternalIDs => People.ExternalIDs; public string ExternalIDs => People.ExternalIDs;
[JsonIgnore] public long ShowID { get; set; } [JsonIgnore] public long ShowID { get; set; }
[JsonIgnore] public virtual Show Show { get; set; } [JsonIgnore] public virtual Show Show { get; set; }
public string Role { get; set; } public string Role { get; set; }
public string Type { get; set; } public string Type { get; set; }
public PeopleLink() {} public PeopleLink() {}
public PeopleLink(People people, Show show, string role, string type) public PeopleLink(People people, Show show, string role, string type)
{ {
People = people; People = people;
Show = show; Show = show;
Role = role; Role = role;
Type = type; Type = type;
} }
public PeopleLink(string slug, string name, string role, string type, string imgPrimary, string externalIDs) public PeopleLink(string slug, string name, string role, string type, string imgPrimary, string externalIDs)
{ {
People = new People(slug, name, imgPrimary, externalIDs); People = new People(slug, name, imgPrimary, externalIDs);
Role = role; Role = role;
Type = type; Type = type;
} }
} }
} }

View File

@ -2,13 +2,13 @@
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class SearchResult public class SearchResult
{ {
public string Query; public string Query;
public IEnumerable<Show> Shows; public IEnumerable<Show> Shows;
public IEnumerable<Episode> Episodes; public IEnumerable<Episode> Episodes;
public IEnumerable<People> People; public IEnumerable<People> People;
public IEnumerable<Genre> Genres; public IEnumerable<Genre> Genres;
public IEnumerable<Studio> Studios; public IEnumerable<Studio> Studios;
} }
} }

View File

@ -3,53 +3,49 @@ using Newtonsoft.Json;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Season : IMergable<Season> public class Season : IMergable<Season>
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long ShowID { get; set; } [JsonIgnore] public long ShowID { get; set; }
public long SeasonNumber { get; set; } = -1; public long SeasonNumber { get; set; } = -1;
public string Title { get; set; } public string Title { get; set; }
public string Overview { get; set; } public string Overview { get; set; }
public long? Year { get; set; } public long? Year { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; } [JsonIgnore] public string ImgPrimary { get; set; }
public string ExternalIDs { get; set; } public string ExternalIDs { get; set; }
[JsonIgnore] public virtual Show Show { get; set; } [JsonIgnore] public virtual Show Show { get; set; }
[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; } [JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; }
public Season() { } public Season() { }
public Season(long showID, long seasonNumber, string title, string overview, long? year, string imgPrimary, string externalIDs) public Season(long showID, long seasonNumber, string title, string overview, long? year, string imgPrimary, string externalIDs)
{ {
ShowID = showID; ShowID = showID;
SeasonNumber = seasonNumber; SeasonNumber = seasonNumber;
Title = title; Title = title;
Overview = overview; Overview = overview;
Year = year; Year = year;
ImgPrimary = imgPrimary; ImgPrimary = imgPrimary;
ExternalIDs = externalIDs; ExternalIDs = externalIDs;
} }
public Season Merge(Season other) public Season Merge(Season other)
{ {
if (other == null) if (other == null)
return this; return this;
if (ShowID == -1) if (ShowID == -1)
ShowID = other.ShowID; ShowID = other.ShowID;
if (SeasonNumber == -1) if (SeasonNumber == -1)
SeasonNumber = other.SeasonNumber; SeasonNumber = other.SeasonNumber;
if (Title == null) Title ??= other.Title;
Title = other.Title; Overview ??= other.Overview;
if (Overview == null) Year ??= other.Year;
Overview = other.Overview; ImgPrimary ??= other.ImgPrimary;
if (Year == null) ExternalIDs += '|' + other.ExternalIDs;
Year = other.Year; return this;
if (ImgPrimary == null) }
ImgPrimary = other.ImgPrimary; }
ExternalIDs += '|' + other.ExternalIDs;
return this;
}
}
} }

View File

@ -5,144 +5,126 @@ using System.Linq;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Show : IMergable<Show> public class Show : IMergable<Show>
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string[] Aliases { get; set; } public string[] Aliases { get; set; }
[JsonIgnore] public string Path { get; set; } [JsonIgnore] public string Path { get; set; }
public string Overview { get; set; } public string Overview { get; set; }
public Status? Status { get; set; } public Status? Status { get; set; }
public string TrailerUrl { get; set; } public string TrailerUrl { get; set; }
public long? StartYear { get; set; } public long? StartYear { get; set; }
public long? EndYear { get; set; } public long? EndYear { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; } [JsonIgnore] public string ImgPrimary { get; set; }
[JsonIgnore] public string ImgThumb { get; set; } [JsonIgnore] public string ImgThumb { get; set; }
[JsonIgnore] public string ImgLogo { get; set; } [JsonIgnore] public string ImgLogo { get; set; }
[JsonIgnore] public string ImgBackdrop { get; set; } [JsonIgnore] public string ImgBackdrop { get; set; }
public string ExternalIDs { get; set; } public string ExternalIDs { get; set; }
public bool IsMovie { get; set; } public bool IsMovie { get; set; }
public bool IsCollection; public bool IsCollection;
[JsonIgnore] public virtual IEnumerable<Genre> Genres [JsonIgnore] public virtual IEnumerable<Genre> Genres
{ {
get { return GenreLinks?.Select(x => x.Genre).OrderBy(x => x.Name); } get { return GenreLinks?.Select(x => x.Genre).OrderBy(x => x.Name); }
set { GenreLinks = value?.Select(x => new GenreLink(this, x)).ToList(); } set { GenreLinks = value?.Select(x => new GenreLink(this, x)).ToList(); }
} }
[JsonIgnore] public virtual List<GenreLink> GenreLinks { get; set; } [JsonIgnore] public virtual List<GenreLink> GenreLinks { get; set; }
[JsonIgnore] public virtual Studio Studio { get; set; } [JsonIgnore] public virtual Studio Studio { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleLink> People { get; set; } [JsonIgnore] public virtual IEnumerable<PeopleLink> People { get; set; }
[JsonIgnore] public virtual IEnumerable<Season> Seasons { get; set; } [JsonIgnore] public virtual IEnumerable<Season> Seasons { get; set; }
[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; } [JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; }
public string GetAliases() public string GetAliases()
{ {
return Aliases == null ? null : string.Join('|', Aliases); return Aliases == null ? null : string.Join('|', Aliases);
} }
public string GetGenres() public string GetGenres()
{ {
return Genres == null ? null : string.Join('|', Genres); return Genres == null ? null : string.Join('|', Genres);
} }
public Show() { } public Show() { }
public Show(string slug, string title, IEnumerable<string> aliases, string path, string overview, string trailerUrl, IEnumerable<Genre> genres, Status? status, long? startYear, long? endYear, string externalIDs) public Show(string slug, string title, IEnumerable<string> aliases, string path, string overview, string trailerUrl, IEnumerable<Genre> genres, Status? status, long? startYear, long? endYear, string externalIDs)
{ {
Slug = slug; Slug = slug;
Title = title; Title = title;
Aliases = aliases.ToArray(); Aliases = aliases.ToArray();
Path = path; Path = path;
Overview = overview; Overview = overview;
TrailerUrl = trailerUrl; TrailerUrl = trailerUrl;
Genres = genres; Genres = genres;
Status = status; Status = status;
StartYear = startYear; StartYear = startYear;
EndYear = endYear; EndYear = endYear;
ExternalIDs = externalIDs; ExternalIDs = externalIDs;
IsCollection = false; IsCollection = false;
} }
public Show(string slug, string title, IEnumerable<string> aliases, string path, string overview, string trailerUrl, Status? status, long? startYear, long? endYear, string imgPrimary, string imgThumb, string imgLogo, string imgBackdrop, string externalIDs) public Show(string slug, string title, IEnumerable<string> aliases, string path, string overview, string trailerUrl, Status? status, long? startYear, long? endYear, string imgPrimary, string imgThumb, string imgLogo, string imgBackdrop, string externalIDs)
{ {
Slug = slug; Slug = slug;
Title = title; Title = title;
Aliases = aliases.ToArray(); Aliases = aliases.ToArray();
Path = path; Path = path;
Overview = overview; Overview = overview;
TrailerUrl = trailerUrl; TrailerUrl = trailerUrl;
Status = status; Status = status;
StartYear = startYear; StartYear = startYear;
EndYear = endYear; EndYear = endYear;
ImgPrimary = imgPrimary; ImgPrimary = imgPrimary;
ImgThumb = imgThumb; ImgThumb = imgThumb;
ImgLogo = imgLogo; ImgLogo = imgLogo;
ImgBackdrop = imgBackdrop; ImgBackdrop = imgBackdrop;
ExternalIDs = externalIDs; ExternalIDs = externalIDs;
IsCollection = false; IsCollection = false;
} }
public string GetID(string provider) public string GetID(string provider)
{ {
if (ExternalIDs?.Contains(provider) != true) if (ExternalIDs?.Contains(provider) != true)
return null; return null;
int startIndex = ExternalIDs.IndexOf(provider, StringComparison.Ordinal) + provider.Length + 1; //The + 1 is for the '=' int startIndex = ExternalIDs.IndexOf(provider, StringComparison.Ordinal) + provider.Length + 1; //The + 1 is for the '='
if (ExternalIDs.IndexOf('|', startIndex) == -1) if (ExternalIDs.IndexOf('|', startIndex) == -1)
return ExternalIDs.Substring(startIndex); return ExternalIDs.Substring(startIndex);
return ExternalIDs.Substring(startIndex, ExternalIDs.IndexOf('|', startIndex) - startIndex); return ExternalIDs.Substring(startIndex, ExternalIDs.IndexOf('|', startIndex) - startIndex);
} }
public Show Merge(Show other) public Show Merge(Show other)
{ {
if (other == null) if (other == null)
return this; return this;
if (ID == -1) if (ID == -1)
ID = other.ID; ID = other.ID;
if (Slug == null) Slug ??= other.Slug;
Slug = other.Slug; Title ??= other.Title;
if (Title == null) Aliases = Aliases == null ? other.Aliases : Aliases.Concat(other.Aliases).ToArray();
Title = other.Title; Genres = Genres == null ? other.Genres : Genres.Concat(other.Genres);
if (Aliases == null) Path ??= other.Path;
Aliases = other.Aliases; Overview ??= other.Overview;
else TrailerUrl ??= other.TrailerUrl;
Aliases = Aliases.Concat(other.Aliases).ToArray(); Status ??= other.Status;
if (Genres == null) StartYear ??= other.StartYear;
Genres = other.Genres; EndYear ??= other.EndYear;
else ImgPrimary ??= other.ImgPrimary;
Genres = Genres.Concat(other.Genres); ImgThumb ??= other.ImgThumb;
if (Path == null) ImgLogo ??= other.ImgLogo;
Path = other.Path; ImgBackdrop ??= other.ImgBackdrop;
if (Overview == null) ExternalIDs += '|' + other.ExternalIDs;
Overview = other.Overview; return this;
if (TrailerUrl == null) }
TrailerUrl = other.TrailerUrl; }
if (Status == null)
Status = other.Status;
if (StartYear == null)
StartYear = other.StartYear;
if (EndYear == null)
EndYear = other.EndYear;
if (ImgPrimary == null)
ImgPrimary = other.ImgPrimary;
if (ImgThumb == null)
ImgThumb = other.ImgThumb;
if (ImgLogo == null)
ImgLogo = other.ImgLogo;
if (ImgBackdrop == null)
ImgBackdrop = other.ImgBackdrop;
ExternalIDs += '|' + other.ExternalIDs;
return this;
}
}
public enum Status { Finished, Airing } public enum Status { Finished, Airing }
} }

View File

@ -2,23 +2,23 @@
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Studio public class Studio
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Name { get; set; } public string Name { get; set; }
public Studio() { } public Studio() { }
public Studio(string slug, string name) public Studio(string slug, string name)
{ {
Slug = slug; Slug = slug;
Name = name; Name = name;
} }
public static Studio Default() public static Studio Default()
{ {
return new Studio("unknow", "Unknow Studio"); return new Studio("unknow", "Unknow Studio");
} }
} }
} }

View File

@ -7,121 +7,121 @@ using System.Runtime.InteropServices;
namespace Kyoo.Models namespace Kyoo.Models
{ {
namespace Watch namespace Watch
{ {
public enum StreamType public enum StreamType
{ {
Unknow = 0, Unknow = 0,
Video = 1, Video = 1,
Audio = 2, Audio = 2,
Subtitle = 3 Subtitle = 3
} }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Stream public class Stream
{ {
public string Title { get; set; } public string Title { get; set; }
public string Language { get; set; } public string Language { get; set; }
public string Codec { get; set; } public string Codec { get; set; }
[MarshalAs(UnmanagedType.I1)] public bool isDefault; [MarshalAs(UnmanagedType.I1)] public bool isDefault;
[MarshalAs(UnmanagedType.I1)] public bool isForced; [MarshalAs(UnmanagedType.I1)] public bool isForced;
[JsonIgnore] public string Path { get; set; } [JsonIgnore] public string Path { get; set; }
[JsonIgnore] public StreamType Type { get; set; } [JsonIgnore] public StreamType Type { get; set; }
public Stream() {} public Stream() {}
public Stream(string title, string language, string codec, bool isDefault, bool isForced, string path, StreamType type) public Stream(string title, string language, string codec, bool isDefault, bool isForced, string path, StreamType type)
{ {
Title = title; Title = title;
Language = language; Language = language;
Codec = codec; Codec = codec;
this.isDefault = isDefault; this.isDefault = isDefault;
this.isForced = isForced; this.isForced = isForced;
Path = path; Path = path;
Type = type; Type = type;
} }
public Stream(Stream stream) public Stream(Stream stream)
{ {
Title = stream.Title; Title = stream.Title;
Language = stream.Language; Language = stream.Language;
isDefault = stream.isDefault; isDefault = stream.isDefault;
isForced = stream.isForced; isForced = stream.isForced;
Codec = stream.Codec; Codec = stream.Codec;
Path = stream.Path; Path = stream.Path;
Type = stream.Type; Type = stream.Type;
} }
} }
} }
public class Track : Stream public class Track : Stream
{ {
[JsonIgnore] public long ID { get; set; } [JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long EpisodeID { get; set; } [JsonIgnore] public long EpisodeID { get; set; }
public bool IsDefault public bool IsDefault
{ {
get => isDefault; get => isDefault;
set => isDefault = value; set => isDefault = value;
} }
public bool IsForced public bool IsForced
{ {
get => isForced; get => isForced;
set => isForced = value; set => isForced = value;
} }
public string DisplayName; public string DisplayName;
public string Link; public string Link;
[JsonIgnore] public bool IsExternal { get; set; } [JsonIgnore] public bool IsExternal { get; set; }
[JsonIgnore] public virtual Episode Episode { get; set; } [JsonIgnore] public virtual Episode Episode { get; set; }
public Track() { } public Track() { }
public Track(StreamType type, string title, string language, bool isDefault, bool isForced, string codec, bool isExternal, string path) public Track(StreamType type, string title, string language, bool isDefault, bool isForced, string codec, bool isExternal, string path)
: base(title, language, codec, isDefault, isForced, path, type) : base(title, language, codec, isDefault, isForced, path, type)
{ {
IsExternal = isExternal; IsExternal = isExternal;
} }
public Track(Stream stream) public Track(Stream stream)
: base(stream) : base(stream)
{ {
IsExternal = false; IsExternal = false;
} }
public Track SetLink(string episodeSlug) public Track SetLink(string episodeSlug)
{ {
if (Type == StreamType.Subtitle) if (Type == StreamType.Subtitle)
{ {
string language = Language; string language = Language;
//Converting mkv track language to c# system language tag. //Converting mkv track language to c# system language tag.
if (language == "fre") if (language == "fre")
language = "fra"; language = "fra";
DisplayName = CultureInfo.GetCultures(CultureTypes.NeutralCultures).FirstOrDefault(x => x.ThreeLetterISOLanguageName == language)?.EnglishName ?? language; DisplayName = CultureInfo.GetCultures(CultureTypes.NeutralCultures).FirstOrDefault(x => x.ThreeLetterISOLanguageName == language)?.EnglishName ?? language;
Link = "/subtitle/" + episodeSlug + "." + Language; Link = "/subtitle/" + episodeSlug + "." + Language;
if (IsForced) if (IsForced)
{ {
DisplayName += " Forced"; DisplayName += " Forced";
Link += "-forced"; Link += "-forced";
} }
if (Title != null && Title.Length > 1) if (Title != null && Title.Length > 1)
DisplayName += " - " + Title; DisplayName += " - " + Title;
switch (Codec) switch (Codec)
{ {
case "ass": case "ass":
Link += ".ass"; Link += ".ass";
break; break;
case "subrip": case "subrip":
Link += ".srt"; Link += ".srt";
break; break;
} }
} }
else else
Link = null; Link = null;
return this; return this;
} }
} }
} }

View File

@ -4,48 +4,48 @@ using System.Collections.Generic;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class WatchItem public class WatchItem
{ {
[JsonIgnore] public readonly long EpisodeID = -1; [JsonIgnore] public readonly long EpisodeID = -1;
public string ShowTitle; public string ShowTitle;
public string ShowSlug; public string ShowSlug;
public long SeasonNumber; public long SeasonNumber;
public long EpisodeNumber; public long EpisodeNumber;
public string Title; public string Title;
public string Link; public string Link;
public DateTime? ReleaseDate; public DateTime? ReleaseDate;
[JsonIgnore] public string Path; [JsonIgnore] public string Path;
public string PreviousEpisode; public string PreviousEpisode;
public Episode NextEpisode; public Episode NextEpisode;
public string Container; public string Container;
public Track Video; public Track Video;
public IEnumerable<Track> Audios; public IEnumerable<Track> Audios;
public IEnumerable<Track> Subtitles; public IEnumerable<Track> Subtitles;
public WatchItem() { } public WatchItem() { }
public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path) public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path)
{ {
EpisodeID = episodeID; EpisodeID = episodeID;
ShowTitle = showTitle; ShowTitle = showTitle;
ShowSlug = showSlug; ShowSlug = showSlug;
SeasonNumber = seasonNumber; SeasonNumber = seasonNumber;
EpisodeNumber = episodeNumber; EpisodeNumber = episodeNumber;
Title = title; Title = title;
ReleaseDate = releaseDate; ReleaseDate = releaseDate;
Path = path; Path = path;
Container = Path.Substring(Path.LastIndexOf('.') + 1); Container = Path.Substring(Path.LastIndexOf('.') + 1);
Link = Episode.GetSlug(ShowSlug, seasonNumber, episodeNumber); Link = Episode.GetSlug(ShowSlug, seasonNumber, episodeNumber);
} }
public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path, Track[] audios, Track[] subtitles) public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path, Track[] audios, Track[] subtitles)
: this(episodeID, showTitle, showSlug, seasonNumber, episodeNumber, title, releaseDate, path) : this(episodeID, showTitle, showSlug, seasonNumber, episodeNumber, title, releaseDate, path)
{ {
Audios = audios; Audios = audios;
Subtitles = subtitles; Subtitles = subtitles;
} }
} }
} }

View File

@ -3,39 +3,39 @@ using Kyoo.Models;
namespace Kyoo namespace Kyoo
{ {
public interface IMergable<T> public interface IMergable<T>
{ {
public T Merge(T other); public T Merge(T other);
} }
public static class Utility public static class Utility
{ {
public static string ToSlug(string name) public static string ToSlug(string name)
{ {
if (name == null) if (name == null)
return null; return null;
//First to lower case //First to lower case
name = name.ToLowerInvariant(); name = name.ToLowerInvariant();
//Remove all accents //Remove all accents
//var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(showTitle); //var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(showTitle);
//showTitle = Encoding.ASCII.GetString(bytes); //showTitle = Encoding.ASCII.GetString(bytes);
//Replace spaces //Replace spaces
name = Regex.Replace(name, @"\s", "-", RegexOptions.Compiled); name = Regex.Replace(name, @"\s", "-", RegexOptions.Compiled);
//Remove invalid chars //Remove invalid chars
name = Regex.Replace(name, @"[^\w\s\p{Pd}]", "", RegexOptions.Compiled); name = Regex.Replace(name, @"[^\w\s\p{Pd}]", "", RegexOptions.Compiled);
//Trim dashes from end //Trim dashes from end
name = name.Trim('-', '_'); name = name.Trim('-', '_');
//Replace double occurences of - or \_ //Replace double occurences of - or \_
name = Regex.Replace(name, @"([-_]){2,}", "$1", RegexOptions.Compiled); name = Regex.Replace(name, @"([-_]){2,}", "$1", RegexOptions.Compiled);
return name; return name;
} }
public static void SetImage(Show show, string imgUrl, ImageType type) public static void SetImage(Show show, string imgUrl, ImageType type)

View File

@ -11,173 +11,173 @@ using Kyoo.Models.Watch;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public class Crawler : ICrawler public class Crawler : ICrawler
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IProviderManager _metadataProvider; private readonly IProviderManager _metadataProvider;
private readonly ITranscoder _transcoder; private readonly ITranscoder _transcoder;
private readonly IConfiguration _config; private readonly IConfiguration _config;
public Crawler(ILibraryManager libraryManager, IProviderManager metadataProvider, ITranscoder transcoder, IConfiguration configuration) public Crawler(ILibraryManager libraryManager, IProviderManager metadataProvider, ITranscoder transcoder, IConfiguration configuration)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_metadataProvider = metadataProvider; _metadataProvider = metadataProvider;
_transcoder = transcoder; _transcoder = transcoder;
_config = configuration; _config = configuration;
} }
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
try try
{ {
IEnumerable<Episode> episodes = _libraryManager.GetAllEpisodes(); IEnumerable<Episode> episodes = _libraryManager.GetAllEpisodes();
IEnumerable<Library> libraries = _libraryManager.GetLibraries(); IEnumerable<Library> libraries = _libraryManager.GetLibraries();
foreach (Episode episode in episodes) foreach (Episode episode in episodes)
{ {
if (!File.Exists(episode.Path)) if (!File.Exists(episode.Path))
_libraryManager.RemoveEpisode(episode.ID); _libraryManager.RemoveEpisode(episode.ID);
} }
foreach (Library library in libraries) foreach (Library library in libraries)
await Scan(library, cancellationToken); await Scan(library, cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.Error.WriteLine($"Unknown exception thrown durring libraries scan.\nException: {ex.Message}"); Console.Error.WriteLine($"Unknown exception thrown durring libraries scan.\nException: {ex.Message}");
} }
Console.WriteLine("Scan finished!"); Console.WriteLine("Scan finished!");
} }
private async Task Scan(Library library, CancellationToken cancellationToken) private async Task Scan(Library library, CancellationToken cancellationToken)
{ {
Console.WriteLine($"Scanning library {library.Name} at {string.Concat(library.Paths)}"); Console.WriteLine($"Scanning library {library.Name} at {string.Concat(library.Paths)}");
foreach (string path in library.Paths) foreach (string path in library.Paths)
{ {
foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories)) foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return;
if (!IsVideo(file) || _libraryManager.IsEpisodeRegistered(file, out long _)) if (!IsVideo(file) || _libraryManager.IsEpisodeRegistered(file, out long _))
continue; continue;
string relativePath = file.Substring(path.Length); string relativePath = file.Substring(path.Length);
await RegisterFile(file, relativePath, library); await RegisterFile(file, relativePath, library);
} }
} }
} }
private async Task RegisterFile(string path, string relativePath, Library library) private async Task RegisterFile(string path, string relativePath, Library library)
{ {
Console.WriteLine("Registering episode at: " + path); Console.WriteLine("Registering episode at: " + path);
string patern = _config.GetValue<string>("regex"); string patern = _config.GetValue<string>("regex");
Regex regex = new Regex(patern, RegexOptions.IgnoreCase); Regex regex = new Regex(patern, RegexOptions.IgnoreCase);
Match match = regex.Match(relativePath); Match match = regex.Match(relativePath);
string showPath = Path.GetDirectoryName(path); string showPath = Path.GetDirectoryName(path);
string collectionName = match.Groups["Collection"]?.Value; string collectionName = match.Groups["Collection"]?.Value;
string showName = match.Groups["ShowTitle"].Value; string showName = match.Groups["ShowTitle"].Value;
long seasonNumber = long.TryParse(match.Groups["Season"].Value, out long tmp) ? tmp : -1; long seasonNumber = long.TryParse(match.Groups["Season"].Value, out long tmp) ? tmp : -1;
long episodeNumber = long.TryParse(match.Groups["Episode"].Value, out tmp) ? tmp : -1; long episodeNumber = long.TryParse(match.Groups["Episode"].Value, out tmp) ? tmp : -1;
long absoluteNumber = long.TryParse(match.Groups["Absolute"].Value, out tmp) ? tmp : -1; long absoluteNumber = long.TryParse(match.Groups["Absolute"].Value, out tmp) ? tmp : -1;
Collection collection = await GetCollection(collectionName, library); Collection collection = await GetCollection(collectionName, library);
bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1; bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1;
Show show = await GetShow(showName, showPath, isMovie, library); Show show = await GetShow(showName, showPath, isMovie, library);
if (isMovie) if (isMovie)
_libraryManager.RegisterShow(show); _libraryManager.RegisterShow(show);
else else
{ {
Season season = await GetSeason(show, seasonNumber, library); Season season = await GetSeason(show, seasonNumber, library);
Episode episode = await GetEpisode(show, season, episodeNumber, absoluteNumber, path, library); Episode episode = await GetEpisode(show, season, episodeNumber, absoluteNumber, path, library);
_libraryManager.RegisterEpisode(episode); _libraryManager.RegisterEpisode(episode);
} }
_libraryManager.RegisterShowLinks(library, collection, show); _libraryManager.RegisterShowLinks(library, collection, show);
} }
private async Task<Collection> GetCollection(string collectionName, Library library) private async Task<Collection> GetCollection(string collectionName, Library library)
{ {
if (string.IsNullOrEmpty(collectionName)) if (string.IsNullOrEmpty(collectionName))
return await Task.FromResult<Collection>(null); return await Task.FromResult<Collection>(null);
return _libraryManager.GetCollection(Utility.ToSlug(collectionName)) ?? await _metadataProvider.GetCollectionFromName(collectionName, library); return _libraryManager.GetCollection(Utility.ToSlug(collectionName)) ?? await _metadataProvider.GetCollectionFromName(collectionName, library);
} }
private async Task<Show> GetShow(string showTitle, string showPath, bool isMovie, Library library) private async Task<Show> GetShow(string showTitle, string showPath, bool isMovie, Library library)
{ {
Show show = _libraryManager.GetShow(showPath); Show show = _libraryManager.GetShow(showPath);
if (show != null) if (show != null)
return show; return show;
show = await _metadataProvider.GetShowFromName(showTitle, showPath, isMovie, library); show = await _metadataProvider.GetShowFromName(showTitle, showPath, isMovie, library);
show.People = (await _metadataProvider.GetPeople(show, library)).GroupBy(x => x.Slug).Select(x => x.First()) show.People = (await _metadataProvider.GetPeople(show, library)).GroupBy(x => x.Slug).Select(x => x.First())
.Select(x => .Select(x =>
{ {
People existing = _libraryManager.GetPeopleBySlug(x.Slug); People existing = _libraryManager.GetPeopleBySlug(x.Slug);
return existing != null ? new PeopleLink(existing, show, x.Role, x.Type) : x; return existing != null ? new PeopleLink(existing, show, x.Role, x.Type) : x;
}).ToList(); }).ToList();
return show; return show;
} }
private async Task<Season> GetSeason(Show show, long seasonNumber, Library library) private async Task<Season> GetSeason(Show show, long seasonNumber, Library library)
{ {
if (seasonNumber == -1) if (seasonNumber == -1)
return null; return null;
Season season = _libraryManager.GetSeason(show.Slug, seasonNumber); Season season = _libraryManager.GetSeason(show.Slug, seasonNumber);
if (season != null) if (season != null)
return await Task.FromResult(season); return await Task.FromResult(season);
season = await _metadataProvider.GetSeason(show, seasonNumber, library); season = await _metadataProvider.GetSeason(show, seasonNumber, library);
season.Show = show; season.Show = show;
return season; return season;
} }
private async Task<Episode> GetEpisode(Show show, Season season, long episodeNumber, long absoluteNumber, string episodePath, Library library) private async Task<Episode> GetEpisode(Show show, Season season, long episodeNumber, long absoluteNumber, string episodePath, Library library)
{ {
Episode episode = await _metadataProvider.GetEpisode(show, episodePath, season?.SeasonNumber ?? -1, episodeNumber, absoluteNumber, library); Episode episode = await _metadataProvider.GetEpisode(show, episodePath, season?.SeasonNumber ?? -1, episodeNumber, absoluteNumber, library);
episode.Show = show; episode.Show = show;
if (season == null) if (season == null)
season = await GetSeason(show, episode.SeasonNumber, library); season = await GetSeason(show, episode.SeasonNumber, library);
episode.Season = season; episode.Season = season;
IEnumerable<Track> tracks = await _transcoder.GetTrackInfo(episode.Path); IEnumerable<Track> tracks = await _transcoder.GetTrackInfo(episode.Path);
List<Track> epTracks = tracks.Where(x => x.Type != StreamType.Subtitle).Concat(GetExtractedSubtitles(episode)).ToList(); List<Track> epTracks = tracks.Where(x => x.Type != StreamType.Subtitle).Concat(GetExtractedSubtitles(episode)).ToList();
if (epTracks.Count(x => !x.IsExternal) < tracks.Count()) if (epTracks.Count(x => !x.IsExternal) < tracks.Count())
epTracks.AddRange(await _transcoder.ExtractSubtitles(episode.Path)); epTracks.AddRange(await _transcoder.ExtractSubtitles(episode.Path));
episode.Tracks = epTracks; episode.Tracks = epTracks;
return episode; return episode;
} }
private static IEnumerable<Track> GetExtractedSubtitles(Episode episode) private static IEnumerable<Track> GetExtractedSubtitles(Episode episode)
{ {
string path = Path.Combine(Path.GetDirectoryName(episode.Path), "Subtitles"); string path = Path.Combine(Path.GetDirectoryName(episode.Path), "Subtitles");
List<Track> tracks = new List<Track>(); List<Track> tracks = new List<Track>();
if (!Directory.Exists(path)) if (!Directory.Exists(path))
return tracks; return tracks;
foreach (string sub in Directory.EnumerateFiles(path, "", SearchOption.AllDirectories)) foreach (string sub in Directory.EnumerateFiles(path, "", SearchOption.AllDirectories))
{ {
string episodeLink = Path.GetFileNameWithoutExtension(episode.Path); string episodeLink = Path.GetFileNameWithoutExtension(episode.Path);
if (!sub.Contains(episodeLink)) if (!sub.Contains(episodeLink))
continue; continue;
string language = sub.Substring(Path.GetDirectoryName(sub).Length + episodeLink.Length + 2, 3); string language = sub.Substring(Path.GetDirectoryName(sub).Length + episodeLink.Length + 2, 3);
bool isDefault = sub.Contains("default"); bool isDefault = sub.Contains("default");
bool isForced = sub.Contains("forced"); bool isForced = sub.Contains("forced");
Track track = new Track(StreamType.Subtitle, null, language, isDefault, isForced, null, false, sub) { EpisodeID = episode.ID }; Track track = new Track(StreamType.Subtitle, null, language, isDefault, isForced, null, false, sub) { EpisodeID = episode.ID };
if (Path.GetExtension(sub) == ".ass") if (Path.GetExtension(sub) == ".ass")
track.Codec = "ass"; track.Codec = "ass";
else if (Path.GetExtension(sub) == ".srt") else if (Path.GetExtension(sub) == ".srt")
track.Codec = "subrip"; track.Codec = "subrip";
else else
track.Codec = null; track.Codec = null;
tracks.Add(track); tracks.Add(track);
} }
return tracks; return tracks;
} }
private static readonly string[] VideoExtensions = { ".webm", ".mkv", ".flv", ".vob", ".ogg", ".ogv", ".avi", ".mts", ".m2ts", ".ts", ".mov", ".qt", ".asf", ".mp4", ".m4p", ".m4v", ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".m2v", ".3gp", ".3g2" }; private static readonly string[] VideoExtensions = { ".webm", ".mkv", ".flv", ".vob", ".ogg", ".ogv", ".avi", ".mts", ".m2ts", ".ts", ".mov", ".qt", ".asf", ".mp4", ".m4p", ".m4v", ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".m2v", ".3gp", ".3g2" };
private static bool IsVideo(string filePath) private static bool IsVideo(string filePath)
{ {
return VideoExtensions.Contains(Path.GetExtension(filePath)); return VideoExtensions.Contains(Path.GetExtension(filePath));
} }
} }
} }

View File

@ -48,9 +48,9 @@ namespace Kyoo.Controllers
if (showID == null) if (showID == null)
return null; return null;
return (from track in _database.Tracks where track.Episode.ShowID == showID return (from track in _database.Tracks where track.Episode.ShowID == showID
&& track.Episode.SeasonNumber == seasonNumber && track.Episode.SeasonNumber == seasonNumber
&& track.Episode.EpisodeNumber == episodeNumber && track.Episode.EpisodeNumber == episodeNumber
&& track.Language == languageTag select track).FirstOrDefault(); && track.Language == languageTag select track).FirstOrDefault();
} }
@ -72,7 +72,7 @@ namespace Kyoo.Controllers
where l.CollectionID == null select show).AsEnumerable().Union( where l.CollectionID == null select show).AsEnumerable().Union(
from collection in _database.Collections select collection.AsShow()) from collection in _database.Collections select collection.AsShow())
.Where(x => EF.Functions.Like(x.Title, $"%{searchQuery}%") .Where(x => EF.Functions.Like(x.Title, $"%{searchQuery}%")
|| EF.Functions.Like(x.GetAliases(), $"%{searchQuery}%")) || EF.Functions.Like(x.GetAliases(), $"%{searchQuery}%"))
.Take(20).OrderBy(x => x.Title); .Take(20).OrderBy(x => x.Title);
} }
@ -99,7 +99,7 @@ namespace Kyoo.Controllers
{ {
return (from season in _database.Seasons return (from season in _database.Seasons
where season.SeasonNumber == seasonNumber where season.SeasonNumber == seasonNumber
&& season.Show.Slug == showSlug && season.Show.Slug == showSlug
select season).FirstOrDefault(); select season).FirstOrDefault();
} }
@ -107,7 +107,7 @@ namespace Kyoo.Controllers
{ {
return (from season in _database.Seasons return (from season in _database.Seasons
where season.SeasonNumber == seasonNumber where season.SeasonNumber == seasonNumber
&& season.Show.Slug == showSlug && season.Show.Slug == showSlug
select season).FirstOrDefault()?.Episodes.Count() ?? 0; select season).FirstOrDefault()?.Episodes.Count() ?? 0;
} }
@ -119,7 +119,7 @@ namespace Kyoo.Controllers
public IEnumerable<Episode> GetEpisodes(string showSlug, long seasonNumber) public IEnumerable<Episode> GetEpisodes(string showSlug, long seasonNumber)
{ {
return (from episode in _database.Episodes where episode.SeasonNumber == seasonNumber return (from episode in _database.Episodes where episode.SeasonNumber == seasonNumber
&& episode.Show.Slug == showSlug select episode) && episode.Show.Slug == showSlug select episode)
.OrderBy(x => x.EpisodeNumber) .OrderBy(x => x.EpisodeNumber)
.Select(x => x.SetLink(showSlug)); .Select(x => x.SetLink(showSlug));
} }
@ -127,20 +127,20 @@ namespace Kyoo.Controllers
public IEnumerable<Episode> GetEpisodes(long showID, long seasonNumber) public IEnumerable<Episode> GetEpisodes(long showID, long seasonNumber)
{ {
return from episode in _database.Episodes where episode.ShowID == showID return from episode in _database.Episodes where episode.ShowID == showID
&& episode.SeasonNumber == seasonNumber select episode.SetLink(episode.Show.Slug); && episode.SeasonNumber == seasonNumber select episode.SetLink(episode.Show.Slug);
} }
public Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber) public Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber)
{ {
return (from episode in _database.Episodes where episode.EpisodeNumber == episodeNumber return (from episode in _database.Episodes where episode.EpisodeNumber == episodeNumber
&& episode.SeasonNumber == seasonNumber && episode.SeasonNumber == seasonNumber
&& episode.Show.Slug == showSlug select episode.SetLink(showSlug)).FirstOrDefault(); && episode.Show.Slug == showSlug select episode.SetLink(showSlug)).FirstOrDefault();
} }
public WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber, bool complete = true) public WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber, bool complete = true)
{ {
WatchItem item = (from episode in _database.Episodes where episode.SeasonNumber == seasonNumber WatchItem item = (from episode in _database.Episodes where episode.SeasonNumber == seasonNumber
&& episode.EpisodeNumber == episodeNumber && episode.Show.Slug == showSlug && episode.EpisodeNumber == episodeNumber && episode.Show.Slug == showSlug
select new WatchItem(episode.ID, select new WatchItem(episode.ID,
episode.Show.Title, episode.Show.Title,
episode.Show.Slug, episode.Show.Slug,

View File

@ -49,16 +49,16 @@ namespace Kyoo.Controllers
} }
public T GetPlugin<T>(string name) public T GetPlugin<T>(string name)
{ {
if (_plugins == null) if (_plugins == null)
return default; return default;
return (T)(from plugin in _plugins where plugin.Name == name && plugin is T select plugin).FirstOrDefault(); return (T)(from plugin in _plugins where plugin.Name == name && plugin is T select plugin).FirstOrDefault();
} }
public IEnumerable<T> GetPlugins<T>() public IEnumerable<T> GetPlugins<T>()
{ {
if (_plugins == null) if (_plugins == null)
return new List<T>(); return new List<T>();
return from plugin in _plugins where plugin is T select (T)plugin; return from plugin in _plugins where plugin is T select (T)plugin;
} }
@ -72,20 +72,20 @@ namespace Kyoo.Controllers
_plugins = pluginsPaths.Select(path => _plugins = pluginsPaths.Select(path =>
{ {
try try
{ {
PluginDependencyLoader loader = new PluginDependencyLoader(Path.GetFullPath(path)); PluginDependencyLoader loader = new PluginDependencyLoader(Path.GetFullPath(path));
Assembly ass = loader.LoadFromAssemblyPath(Path.GetFullPath(path)); Assembly ass = loader.LoadFromAssemblyPath(Path.GetFullPath(path));
return (from type in ass.GetTypes() return (from type in ass.GetTypes()
where typeof(IPlugin).IsAssignableFrom(type) where typeof(IPlugin).IsAssignableFrom(type)
select (IPlugin) ActivatorUtilities.CreateInstance(_provider, type)).FirstOrDefault(); select (IPlugin) ActivatorUtilities.CreateInstance(_provider, type)).FirstOrDefault();
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.Error.WriteLine($"Error loading the plugin at {path}.\nException: {ex.Message}"); Console.Error.WriteLine($"Error loading the plugin at {path}.\nException: {ex.Message}");
return null; return null;
} }
}).Where(x => x != null).ToList(); }).Where(x => x != null).ToList();
} }
} }
} }

View File

@ -6,96 +6,96 @@ using System.Threading.Tasks;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public class ProviderManager : IProviderManager public class ProviderManager : IProviderManager
{ {
private readonly IEnumerable<IMetadataProvider> _providers; private readonly IEnumerable<IMetadataProvider> _providers;
private readonly IThumbnailsManager _thumbnailsManager; private readonly IThumbnailsManager _thumbnailsManager;
public ProviderManager(IThumbnailsManager thumbnailsManager, IPluginManager pluginManager) public ProviderManager(IThumbnailsManager thumbnailsManager, IPluginManager pluginManager)
{ {
_thumbnailsManager = thumbnailsManager; _thumbnailsManager = thumbnailsManager;
_providers = pluginManager.GetPlugins<IMetadataProvider>(); _providers = pluginManager.GetPlugins<IMetadataProvider>();
} }
public async Task<T> GetMetadata<T>(Func<IMetadataProvider, Task<T>> providerCall, Library library, string what) where T : IMergable<T>, new() public async Task<T> GetMetadata<T>(Func<IMetadataProvider, Task<T>> providerCall, Library library, string what) where T : IMergable<T>, new()
{ {
T ret = new T(); T ret = new T();
foreach (IMetadataProvider provider in _providers.OrderBy(provider => Array.IndexOf(library.Providers, provider.Name))) foreach (IMetadataProvider provider in _providers.OrderBy(provider => Array.IndexOf(library.Providers, provider.Name)))
{ {
try try
{ {
if (library.Providers.Contains(provider.Name)) if (library.Providers.Contains(provider.Name))
ret = ret.Merge(await providerCall(provider)); ret = ret.Merge(await providerCall(provider));
} catch (Exception ex) { } catch (Exception ex) {
Console.Error.WriteLine($"The provider {provider.Name} coudln't work for {what}. Exception: {ex.Message}"); Console.Error.WriteLine($"The provider {provider.Name} coudln't work for {what}. Exception: {ex.Message}");
} }
} }
return ret; return ret;
} }
public async Task<IEnumerable<T>> GetMetadata<T>(Func<IMetadataProvider, Task<IEnumerable<T>>> providerCall, Library library, string what) public async Task<IEnumerable<T>> GetMetadata<T>(Func<IMetadataProvider, Task<IEnumerable<T>>> providerCall, Library library, string what)
{ {
List<T> ret = new List<T>(); List<T> ret = new List<T>();
foreach (IMetadataProvider provider in _providers.OrderBy(provider => Array.IndexOf(library.Providers, provider.Name))) foreach (IMetadataProvider provider in _providers.OrderBy(provider => Array.IndexOf(library.Providers, provider.Name)))
{ {
try try
{ {
if (library.Providers.Contains(provider.Name)) if (library.Providers.Contains(provider.Name))
ret.AddRange(await providerCall(provider)); ret.AddRange(await providerCall(provider));
} catch (Exception ex) { } catch (Exception ex) {
Console.Error.WriteLine($"The provider {provider.Name} coudln't work for {what}. Exception: {ex.Message}"); Console.Error.WriteLine($"The provider {provider.Name} coudln't work for {what}. Exception: {ex.Message}");
} }
} }
return ret; return ret;
} }
public async Task<Collection> GetCollectionFromName(string name, Library library) public async Task<Collection> GetCollectionFromName(string name, Library library)
{ {
Collection collection = await GetMetadata(provider => provider.GetCollectionFromName(name), library, $"the collection {name}"); Collection collection = await GetMetadata(provider => provider.GetCollectionFromName(name), library, $"the collection {name}");
collection.Name ??= name; collection.Name ??= name;
collection.Slug ??= Utility.ToSlug(name); collection.Slug ??= Utility.ToSlug(name);
return collection; return collection;
} }
public async Task<Show> GetShowFromName(string showName, string showPath, bool isMovie, Library library) public async Task<Show> GetShowFromName(string showName, string showPath, bool isMovie, Library library)
{ {
Show show = await GetMetadata(provider => provider.GetShowFromName(showName, isMovie), library, $"the show {showName}"); Show show = await GetMetadata(provider => provider.GetShowFromName(showName, isMovie), library, $"the show {showName}");
show.Path = showPath; show.Path = showPath;
show.Slug = Utility.ToSlug(showName); show.Slug = Utility.ToSlug(showName);
show.Title ??= showName; show.Title ??= showName;
show.IsMovie = isMovie; show.IsMovie = isMovie;
await _thumbnailsManager.Validate(show); await _thumbnailsManager.Validate(show);
return show; return show;
} }
public async Task<Season> GetSeason(Show show, long seasonNumber, Library library) public async Task<Season> GetSeason(Show show, long seasonNumber, Library library)
{ {
Season season = await GetMetadata(provider => provider.GetSeason(show, seasonNumber), library, $"the season {seasonNumber} of {show.Title}"); Season season = await GetMetadata(provider => provider.GetSeason(show, seasonNumber), library, $"the season {seasonNumber} of {show.Title}");
season.ShowID = show.ID; season.ShowID = show.ID;
season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber; season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber;
season.Title ??= $"Season {season.SeasonNumber}"; season.Title ??= $"Season {season.SeasonNumber}";
return season; return season;
} }
public async Task<Episode> GetEpisode(Show show, string episodePath, long seasonNumber, long episodeNumber, long absoluteNumber, Library library) public async Task<Episode> GetEpisode(Show show, string episodePath, long seasonNumber, long episodeNumber, long absoluteNumber, Library library)
{ {
Episode episode = await GetMetadata(provider => provider.GetEpisode(show, seasonNumber, episodeNumber, absoluteNumber), library, "an episode"); Episode episode = await GetMetadata(provider => provider.GetEpisode(show, seasonNumber, episodeNumber, absoluteNumber), library, "an episode");
episode.ShowID = show.ID; episode.ShowID = show.ID;
episode.Path = episodePath; episode.Path = episodePath;
episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber; episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber;
episode.EpisodeNumber = episode.EpisodeNumber != -1 ? episode.EpisodeNumber : episodeNumber; episode.EpisodeNumber = episode.EpisodeNumber != -1 ? episode.EpisodeNumber : episodeNumber;
episode.AbsoluteNumber = episode.AbsoluteNumber != -1 ? episode.AbsoluteNumber : absoluteNumber; episode.AbsoluteNumber = episode.AbsoluteNumber != -1 ? episode.AbsoluteNumber : absoluteNumber;
await _thumbnailsManager.Validate(episode); await _thumbnailsManager.Validate(episode);
return episode; return episode;
} }
public async Task<IEnumerable<PeopleLink>> GetPeople(Show show, Library library) public async Task<IEnumerable<PeopleLink>> GetPeople(Show show, Library library)
{ {
IEnumerable<PeopleLink> people = await GetMetadata(provider => provider.GetPeople(show), library, "unknown data"); IEnumerable<PeopleLink> people = await GetMetadata(provider => provider.GetPeople(show), library, "unknown data");
people = await _thumbnailsManager.Validate(people.ToList()); people = await _thumbnailsManager.Validate(people.ToList());
return people; return people;
} }
} }
} }

View File

@ -8,110 +8,110 @@ using System.Threading.Tasks;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public class ThumbnailsManager : IThumbnailsManager public class ThumbnailsManager : IThumbnailsManager
{ {
private readonly IConfiguration _config; private readonly IConfiguration _config;
public ThumbnailsManager(IConfiguration configuration) public ThumbnailsManager(IConfiguration configuration)
{ {
_config = configuration; _config = configuration;
} }
public async Task<Show> Validate(Show show) public async Task<Show> Validate(Show show)
{ {
if (show?.Path == null) if (show?.Path == null)
return null; return null;
string localThumb = Path.Combine(show.Path, "poster.jpg"); string localThumb = Path.Combine(show.Path, "poster.jpg");
string localLogo = Path.Combine(show.Path, "logo.png"); string localLogo = Path.Combine(show.Path, "logo.png");
string localBackdrop = Path.Combine(show.Path, "backdrop.jpg"); string localBackdrop = Path.Combine(show.Path, "backdrop.jpg");
if (show.ImgPrimary != null && !File.Exists(localThumb)) if (show.ImgPrimary != null && !File.Exists(localThumb))
{ {
try try
{ {
using WebClient client = new WebClient(); using WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(show.ImgPrimary), localThumb); await client.DownloadFileTaskAsync(new Uri(show.ImgPrimary), localThumb);
} }
catch (WebException exception) catch (WebException exception)
{ {
Console.Error.WriteLine($"\tThe poster of {show.Title} could not be downloaded.\n\tError: {exception.Message}. "); Console.Error.WriteLine($"\tThe poster of {show.Title} could not be downloaded.\n\tError: {exception.Message}. ");
} }
} }
if (show.ImgLogo != null && !File.Exists(localLogo)) if (show.ImgLogo != null && !File.Exists(localLogo))
{ {
try try
{ {
using WebClient client = new WebClient(); using WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(show.ImgLogo), localLogo); await client.DownloadFileTaskAsync(new Uri(show.ImgLogo), localLogo);
} }
catch (WebException exception) catch (WebException exception)
{ {
Console.Error.WriteLine($"\tThe logo of {show.Title} could not be downloaded.\n\tError: {exception.Message}. "); Console.Error.WriteLine($"\tThe logo of {show.Title} could not be downloaded.\n\tError: {exception.Message}. ");
} }
} }
if (show.ImgBackdrop != null && !File.Exists(localBackdrop)) if (show.ImgBackdrop != null && !File.Exists(localBackdrop))
{ {
try try
{ {
using WebClient client = new WebClient(); using WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(show.ImgBackdrop), localBackdrop); await client.DownloadFileTaskAsync(new Uri(show.ImgBackdrop), localBackdrop);
} }
catch (WebException exception) catch (WebException exception)
{ {
Console.Error.WriteLine($"\tThe backdrop of {show.Title} could not be downloaded.\n\tError: {exception.Message}. "); Console.Error.WriteLine($"\tThe backdrop of {show.Title} could not be downloaded.\n\tError: {exception.Message}. ");
} }
} }
return show; return show;
} }
public async Task<IEnumerable<PeopleLink>> Validate(List<PeopleLink> people) public async Task<IEnumerable<PeopleLink>> Validate(List<PeopleLink> people)
{ {
if (people == null) if (people == null)
return null; return null;
foreach (PeopleLink peop in people) foreach (PeopleLink peop in people)
{ {
string root = _config.GetValue<string>("peoplePath"); string root = _config.GetValue<string>("peoplePath");
Directory.CreateDirectory(root); Directory.CreateDirectory(root);
string localThumb = root + "/" + peop.People.Slug + ".jpg"; string localThumb = root + "/" + peop.People.Slug + ".jpg";
if (peop.People.ImgPrimary == null || File.Exists(localThumb)) if (peop.People.ImgPrimary == null || File.Exists(localThumb))
continue; continue;
try try
{ {
using WebClient client = new WebClient(); using WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(peop.People.ImgPrimary), localThumb); await client.DownloadFileTaskAsync(new Uri(peop.People.ImgPrimary), localThumb);
} }
catch (WebException exception) catch (WebException exception)
{ {
Console.Error.WriteLine($"\tThe profile picture of {peop.People.Name} could not be downloaded.\n\tError: {exception.Message}. "); Console.Error.WriteLine($"\tThe profile picture of {peop.People.Name} could not be downloaded.\n\tError: {exception.Message}. ");
} }
} }
return people; return people;
} }
public async Task<Episode> Validate(Episode episode) public async Task<Episode> Validate(Episode episode)
{ {
if (episode == null || episode.Path == null) if (episode == null || episode.Path == null)
return null; return null;
string localThumb = Path.ChangeExtension(episode.Path, "jpg"); string localThumb = Path.ChangeExtension(episode.Path, "jpg");
if (episode.ImgPrimary == null || File.Exists(localThumb)) if (episode.ImgPrimary == null || File.Exists(localThumb))
return episode; return episode;
try try
{ {
using WebClient client = new WebClient(); using WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(episode.ImgPrimary), localThumb); await client.DownloadFileTaskAsync(new Uri(episode.ImgPrimary), localThumb);
} }
catch (WebException exception) catch (WebException exception)
{ {
Console.Error.WriteLine($"\tThe thumbnail of {episode.Show.Title} s{episode.SeasonNumber}e{episode.EpisodeNumber} could not be downloaded.\n\tError: {exception.Message}. "); Console.Error.WriteLine($"\tThe thumbnail of {episode.Show.Title} s{episode.SeasonNumber}e{episode.EpisodeNumber} could not be downloaded.\n\tError: {exception.Message}. ");
} }
return episode; return episode;
} }
} }
} }

View File

@ -13,97 +13,97 @@ namespace Kyoo.Controllers
{ {
public class BadTranscoderException : Exception {} public class BadTranscoderException : Exception {}
public class Transcoder : ITranscoder public class Transcoder : ITranscoder
{ {
private readonly string _transmuxPath; private readonly string _transmuxPath;
private readonly string _transcodePath; private readonly string _transcodePath;
public Transcoder(IConfiguration config) public Transcoder(IConfiguration config)
{ {
_transmuxPath = config.GetValue<string>("transmuxTempPath"); _transmuxPath = config.GetValue<string>("transmuxTempPath");
_transcodePath = config.GetValue<string>("transcodeTempPath"); _transcodePath = config.GetValue<string>("transcodeTempPath");
if (TranscoderAPI.init() != Marshal.SizeOf<Models.Watch.Stream>()) if (TranscoderAPI.init() != Marshal.SizeOf<Models.Watch.Stream>())
throw new BadTranscoderException(); throw new BadTranscoderException();
} }
public async Task<Track[]> GetTrackInfo(string path) public async Task<Track[]> GetTrackInfo(string path)
{ {
return await Task.Run(() => return await Task.Run(() =>
{ {
TranscoderAPI.GetTrackInfo(path, out Track[] tracks); TranscoderAPI.GetTrackInfo(path, out Track[] tracks);
return tracks; return tracks;
}); });
} }
public async Task<Track[]> ExtractSubtitles(string path) public async Task<Track[]> ExtractSubtitles(string path)
{ {
string output = Path.Combine(Path.GetDirectoryName(path), "Subtitles"); string output = Path.Combine(Path.GetDirectoryName(path), "Subtitles");
Directory.CreateDirectory(output); Directory.CreateDirectory(output);
return await Task.Run(() => return await Task.Run(() =>
{ {
TranscoderAPI.ExtractSubtitles(path, output, out Track[] tracks); TranscoderAPI.ExtractSubtitles(path, output, out Track[] tracks);
return tracks; return tracks;
}); });
} }
public async Task<string> Transmux(WatchItem episode) public async Task<string> Transmux(WatchItem episode)
{ {
string folder = Path.Combine(_transmuxPath, episode.Link); string folder = Path.Combine(_transmuxPath, episode.Link);
string manifest = Path.Combine(folder, episode.Link + ".m3u8"); string manifest = Path.Combine(folder, episode.Link + ".m3u8");
float playableDuration = 0; float playableDuration = 0;
bool transmuxFailed = false; bool transmuxFailed = false;
try try
{ {
Directory.CreateDirectory(folder); Directory.CreateDirectory(folder);
Debug.WriteLine("&Transmuxing " + episode.Link + " at " + episode.Path + ", outputPath: " + folder); Debug.WriteLine("&Transmuxing " + episode.Link + " at " + episode.Path + ", outputPath: " + folder);
if (File.Exists(manifest)) if (File.Exists(manifest))
return manifest; return manifest;
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
{ {
Console.Error.WriteLine($"Access to the path {manifest} is denied. Please change your transmux path in the config."); Console.Error.WriteLine($"Access to the path {manifest} is denied. Please change your transmux path in the config.");
return null; return null;
} }
Task.Run(() => Task.Run(() =>
{ {
transmuxFailed = TranscoderAPI.transmux(episode.Path, manifest.Replace('\\', '/'), out playableDuration) != 0; transmuxFailed = TranscoderAPI.transmux(episode.Path, manifest.Replace('\\', '/'), out playableDuration) != 0;
}); });
while (playableDuration < 10 || (!File.Exists(manifest) && !transmuxFailed)) while (playableDuration < 10 || (!File.Exists(manifest) && !transmuxFailed))
await Task.Delay(10); await Task.Delay(10);
return transmuxFailed ? null : manifest; return transmuxFailed ? null : manifest;
} }
public async Task<string> Transcode(WatchItem episode) public async Task<string> Transcode(WatchItem episode)
{ {
string folder = Path.Combine(_transcodePath, episode.Link); string folder = Path.Combine(_transcodePath, episode.Link);
string manifest = Path.Combine(folder, episode.Link + ".m3u8"); string manifest = Path.Combine(folder, episode.Link + ".m3u8");
float playableDuration = 0; float playableDuration = 0;
bool transmuxFailed = false; bool transmuxFailed = false;
try try
{ {
Directory.CreateDirectory(folder); Directory.CreateDirectory(folder);
Debug.WriteLine("&Transcoding " + episode.Link + " at " + episode.Path + ", outputPath: " + folder); Debug.WriteLine("&Transcoding " + episode.Link + " at " + episode.Path + ", outputPath: " + folder);
if (File.Exists(manifest)) if (File.Exists(manifest))
return manifest; return manifest;
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
{ {
Console.Error.WriteLine($"Access to the path {manifest} is denied. Please change your transmux path in the config."); Console.Error.WriteLine($"Access to the path {manifest} is denied. Please change your transmux path in the config.");
return null; return null;
} }
Task.Run(() => Task.Run(() =>
{ {
transmuxFailed = TranscoderAPI.transcode(episode.Path, manifest.Replace('\\', '/'), out playableDuration) != 0; transmuxFailed = TranscoderAPI.transcode(episode.Path, manifest.Replace('\\', '/'), out playableDuration) != 0;
}); });
while (playableDuration < 10 || (!File.Exists(manifest) && !transmuxFailed)) while (playableDuration < 10 || (!File.Exists(manifest) && !transmuxFailed))
await Task.Delay(10); await Task.Delay(10);
return transmuxFailed ? null : manifest; return transmuxFailed ? null : manifest;
} }
} }
} }

View File

@ -8,88 +8,88 @@ using Kyoo.Models.Watch;
namespace Kyoo.Controllers.TranscoderLink namespace Kyoo.Controllers.TranscoderLink
{ {
public static class TranscoderAPI public static class TranscoderAPI
{ {
private const string TranscoderPath = "libtranscoder.so"; private const string TranscoderPath = "libtranscoder.so";
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int init(); public static extern int init();
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int transmux(string path, string out_path, out float playableDuration); public static extern int transmux(string path, string out_path, out float playableDuration);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int transcode(string path, string out_path, out float playableDuration); public static extern int transcode(string path, string out_path, out float playableDuration);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_track_info(string path, out int array_length, out int track_count); private static extern IntPtr get_track_info(string path, out int array_length, out int track_count);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr extract_subtitles(string path, string out_path, out int array_length, out int track_count); private static extern IntPtr extract_subtitles(string path, string out_path, out int array_length, out int track_count);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern void free_streams(IntPtr stream_ptr); private static extern void free_streams(IntPtr stream_ptr);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern void free(IntPtr ptr); private static extern void free(IntPtr ptr);
public static void GetTrackInfo(string path, out Track[] tracks) public static void GetTrackInfo(string path, out Track[] tracks)
{ {
int size = Marshal.SizeOf<Stream>(); int size = Marshal.SizeOf<Stream>();
IntPtr ptr = get_track_info(path, out int arrayLength, out int trackCount); IntPtr ptr = get_track_info(path, out int arrayLength, out int trackCount);
IntPtr streamsPtr = ptr; IntPtr streamsPtr = ptr;
if (trackCount > 0 && ptr != IntPtr.Zero) if (trackCount > 0 && ptr != IntPtr.Zero)
{ {
tracks = new Track[trackCount]; tracks = new Track[trackCount];
int j = 0; int j = 0;
for (int i = 0; i < arrayLength; i++) for (int i = 0; i < arrayLength; i++)
{ {
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr); Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
if (stream.Type != StreamType.Unknow) if (stream.Type != StreamType.Unknow)
{ {
tracks[j] = new Track(stream); tracks[j] = new Track(stream);
j++; j++;
} }
streamsPtr += size; streamsPtr += size;
} }
} }
else else
tracks = new Track[0]; tracks = new Track[0];
free(ptr); free(ptr);
Console.WriteLine($"\t{tracks.Length} tracks got at: {path}"); Console.WriteLine($"\t{tracks.Length} tracks got at: {path}");
} }
public static void ExtractSubtitles(string path, string outPath, out Track[] tracks) public static void ExtractSubtitles(string path, string outPath, out Track[] tracks)
{ {
int size = Marshal.SizeOf<Stream>(); int size = Marshal.SizeOf<Stream>();
IntPtr ptr = extract_subtitles(path, outPath, out int arrayLength, out int trackCount); IntPtr ptr = extract_subtitles(path, outPath, out int arrayLength, out int trackCount);
IntPtr streamsPtr = ptr; IntPtr streamsPtr = ptr;
if (trackCount > 0 && ptr != IntPtr.Zero) if (trackCount > 0 && ptr != IntPtr.Zero)
{ {
tracks = new Track[trackCount]; tracks = new Track[trackCount];
int j = 0; int j = 0;
for (int i = 0; i < arrayLength; i++) for (int i = 0; i < arrayLength; i++)
{ {
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr); Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
if (stream.Type != StreamType.Unknow) if (stream.Type != StreamType.Unknow)
{ {
tracks[j] = new Track(stream); tracks[j] = new Track(stream);
j++; j++;
} }
streamsPtr += size; streamsPtr += size;
} }
} }
else else
tracks = new Track[0]; tracks = new Track[0];
free(ptr); free(ptr);
Console.WriteLine($"\t{tracks.Count(x => x.Type == StreamType.Subtitle)} subtitles got at: {path}"); Console.WriteLine($"\t{tracks.Count(x => x.Type == StreamType.Subtitle)} subtitles got at: {path}");
} }
} }
} }

View File

@ -7,65 +7,65 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Kyoo namespace Kyoo
{ {
public class DatabaseContext : DbContext public class DatabaseContext : DbContext
{ {
public DatabaseContext(DbContextOptions options) : base(options) { } public DatabaseContext(DbContextOptions options) : base(options) { }
public DbSet<Library> Libraries { get; set; } public DbSet<Library> Libraries { get; set; }
public DbSet<Collection> Collections { get; set; } public DbSet<Collection> Collections { get; set; }
public DbSet<Show> Shows { get; set; } public DbSet<Show> Shows { get; set; }
public DbSet<Season> Seasons { get; set; } public DbSet<Season> Seasons { get; set; }
public DbSet<Episode> Episodes { get; set; } public DbSet<Episode> Episodes { get; set; }
public DbSet<Track> Tracks { get; set; } public DbSet<Track> Tracks { get; set; }
public DbSet<Genre> Genres { get; set; } public DbSet<Genre> Genres { get; set; }
public DbSet<People> Peoples { get; set; } public DbSet<People> Peoples { get; set; }
public DbSet<Studio> Studios { get; set; } public DbSet<Studio> Studios { get; set; }
public DbSet<LibraryLink> LibraryLinks { get; set; } public DbSet<LibraryLink> LibraryLinks { get; set; }
public DbSet<CollectionLink> CollectionLinks { get; set; } public DbSet<CollectionLink> CollectionLinks { get; set; }
public DbSet<PeopleLink> PeopleLinks { get; set; } public DbSet<PeopleLink> PeopleLinks { get; set; }
// This is used because EF doesn't support Many-To-Many relationships so for now we need to override the getter/setters to store this. // This is used because EF doesn't support Many-To-Many relationships so for now we need to override the getter/setters to store this.
public DbSet<GenreLink> GenreLinks { get; set; } public DbSet<GenreLink> GenreLinks { get; set; }
private ValueConverter<string[], string> stringArrayConverter = new ValueConverter<string[], string>( private ValueConverter<string[], string> stringArrayConverter = new ValueConverter<string[], string>(
arr => string.Join("|", arr), arr => string.Join("|", arr),
str => str.Split("|", StringSplitOptions.None)); str => str.Split("|", StringSplitOptions.None));
private ValueComparer<string[]> stringArrayComparer = new ValueComparer<string[]>( private ValueComparer<string[]> stringArrayComparer = new ValueComparer<string[]>(
(l1, l2) => l1.SequenceEqual(l2), (l1, l2) => l1.SequenceEqual(l2),
arr => arr.Aggregate(0, (i, s) => s.GetHashCode())); arr => arr.Aggregate(0, (i, s) => s.GetHashCode()));
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Library>().Property(e => e.Paths).HasConversion(stringArrayConverter).Metadata.SetValueComparer(stringArrayComparer); modelBuilder.Entity<Library>().Property(e => e.Paths).HasConversion(stringArrayConverter).Metadata.SetValueComparer(stringArrayComparer);
modelBuilder.Entity<Library>().Property(e => e.Providers).HasConversion(stringArrayConverter).Metadata.SetValueComparer(stringArrayComparer); modelBuilder.Entity<Library>().Property(e => e.Providers).HasConversion(stringArrayConverter).Metadata.SetValueComparer(stringArrayComparer);
modelBuilder.Entity<Show>().Property(e => e.Aliases).HasConversion(stringArrayConverter).Metadata.SetValueComparer(stringArrayComparer); modelBuilder.Entity<Show>().Property(e => e.Aliases).HasConversion(stringArrayConverter).Metadata.SetValueComparer(stringArrayComparer);
modelBuilder.Entity<Track>() modelBuilder.Entity<Track>()
.Property(t => t.IsDefault) .Property(t => t.IsDefault)
.ValueGeneratedNever(); .ValueGeneratedNever();
modelBuilder.Entity<Track>() modelBuilder.Entity<Track>()
.Property(t => t.IsForced) .Property(t => t.IsForced)
.ValueGeneratedNever(); .ValueGeneratedNever();
modelBuilder.Entity<People>() modelBuilder.Entity<People>()
.HasKey(x => x.Slug); .HasKey(x => x.Slug);
modelBuilder.Entity<GenreLink>() modelBuilder.Entity<GenreLink>()
.HasKey(x => new {x.ShowID, x.GenreID}); .HasKey(x => new {x.ShowID, x.GenreID});
modelBuilder.Entity<Show>() modelBuilder.Entity<Show>()
.Ignore(x => x.Genres); .Ignore(x => x.Genres);
// modelBuilder.Entity<Genre>() // modelBuilder.Entity<Genre>()
// .Ignore(x => x.Shows); // .Ignore(x => x.Shows);
} }
} }
} }
public static class DbSetExtension public static class DbSetExtension

View File

@ -4,17 +4,17 @@ using Kyoo.Controllers;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class AdminController : ControllerBase public class AdminController : ControllerBase
{ {
[HttpGet("scan")] [HttpGet("scan")]
public IActionResult ScanLibrary([FromServices] ICrawler crawler) public IActionResult ScanLibrary([FromServices] ICrawler crawler)
{ {
// The crawler is destroyed before the completion of this task. // The crawler is destroyed before the completion of this task.
// TODO implement an hosted service that can queue tasks from the controller. // TODO implement an hosted service that can queue tasks from the controller.
crawler.StartAsync(new CancellationToken()); crawler.StartAsync(new CancellationToken());
return Ok("Scanning"); return Ok("Scanning");
} }
} }
} }

View File

@ -5,12 +5,12 @@ using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Api namespace Kyoo.Api
{ {
public class AuthentificationAPI : Controller public class AuthentificationAPI : Controller
{ {
// [Authorize, HttpGet("/connect/authorize")] // [Authorize, HttpGet("/connect/authorize")]
// public async Task<IActionResult> Authorize(CancellationToken token) // public async Task<IActionResult> Authorize(CancellationToken token)
// { // {
// //HttpContext.GetOpenIdConnectResponse() // //HttpContext.GetOpenIdConnectResponse()
// } // }
} }
} }

View File

@ -5,26 +5,26 @@ using System.Collections.Generic;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class CollectionController : ControllerBase public class CollectionController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public CollectionController(ILibraryManager libraryManager) public CollectionController(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{collectionSlug}")] [HttpGet("{collectionSlug}")]
public ActionResult<Collection> GetShows(string collectionSlug) public ActionResult<Collection> GetShows(string collectionSlug)
{ {
Collection collection = _libraryManager.GetCollection(collectionSlug); Collection collection = _libraryManager.GetCollection(collectionSlug);
if (collection == null) if (collection == null)
return NotFound(); return NotFound();
return collection; return collection;
} }
} }
} }

View File

@ -6,38 +6,38 @@ using Kyoo.Controllers;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class EpisodesController : ControllerBase public class EpisodesController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public EpisodesController(ILibraryManager libraryManager) public EpisodesController(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{showSlug}/season/{seasonNumber}")] [HttpGet("{showSlug}/season/{seasonNumber}")]
public ActionResult<IEnumerable<Episode>> GetEpisodesForSeason(string showSlug, long seasonNumber) public ActionResult<IEnumerable<Episode>> GetEpisodesForSeason(string showSlug, long seasonNumber)
{ {
IEnumerable<Episode> episodes = _libraryManager.GetEpisodes(showSlug, seasonNumber); IEnumerable<Episode> episodes = _libraryManager.GetEpisodes(showSlug, seasonNumber);
if(episodes == null) if(episodes == null)
return NotFound(); return NotFound();
return episodes.ToList(); return episodes.ToList();
} }
[HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")] [HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")]
[JsonDetailed] [JsonDetailed]
public ActionResult<Episode> GetEpisode(string showSlug, long seasonNumber, long episodeNumber) public ActionResult<Episode> GetEpisode(string showSlug, long seasonNumber, long episodeNumber)
{ {
Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode == null) if (episode == null)
return NotFound(); return NotFound();
return episode; return episode;
} }
} }
} }

View File

@ -6,32 +6,32 @@ using System.Linq;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/libraries")] [Route("api/libraries")]
[ApiController] [ApiController]
public class LibrariesController : ControllerBase public class LibrariesController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public LibrariesController(ILibraryManager libraryManager) public LibrariesController(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet] [HttpGet]
public IEnumerable<Library> GetLibraries() public IEnumerable<Library> GetLibraries()
{ {
return _libraryManager.GetLibraries(); return _libraryManager.GetLibraries();
} }
[HttpGet("{librarySlug}")] [HttpGet("{librarySlug}")]
public ActionResult<IEnumerable<Show>> GetShows(string librarySlug) public ActionResult<IEnumerable<Show>> GetShows(string librarySlug)
{ {
Library library = _libraryManager.GetLibrary(librarySlug); Library library = _libraryManager.GetLibrary(librarySlug);
if (library == null) if (library == null)
return NotFound(); return NotFound();
return _libraryManager.GetShowsInLibrary(library.ID).ToList(); return _libraryManager.GetShowsInLibrary(library.ID).ToList();
} }
} }
} }

View File

@ -4,30 +4,30 @@ using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class PeopleController : ControllerBase public class PeopleController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public PeopleController(ILibraryManager libraryManager) public PeopleController(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{peopleSlug}")] [HttpGet("{peopleSlug}")]
public ActionResult<Collection> GetPeople(string peopleSlug) public ActionResult<Collection> GetPeople(string peopleSlug)
{ {
People people = _libraryManager.GetPeopleBySlug(peopleSlug); People people = _libraryManager.GetPeopleBySlug(peopleSlug);
if (people == null) if (people == null)
return NotFound(); return NotFound();
Collection collection = new Collection(people.Slug, people.Name, null, null) Collection collection = new Collection(people.Slug, people.Name, null, null)
{ {
Shows = _libraryManager.GetShowsByPeople(people.Slug), Shows = _libraryManager.GetShowsByPeople(people.Slug),
Poster = "peopleimg/" + people.Slug Poster = "peopleimg/" + people.Slug
}; };
return collection; return collection;
} }
} }
} }

View File

@ -4,30 +4,30 @@ using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class SearchController : ControllerBase public class SearchController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public SearchController(ILibraryManager libraryManager) public SearchController(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{query}")] [HttpGet("{query}")]
public ActionResult<SearchResult> Search(string query) public ActionResult<SearchResult> Search(string query)
{ {
SearchResult result = new SearchResult SearchResult result = new SearchResult
{ {
Query = query, Query = query,
Shows = _libraryManager.GetShows(query), Shows = _libraryManager.GetShows(query),
Episodes = _libraryManager.SearchEpisodes(query), Episodes = _libraryManager.SearchEpisodes(query),
People = _libraryManager.SearchPeople(query), People = _libraryManager.SearchPeople(query),
Genres = _libraryManager.SearchGenres(query), Genres = _libraryManager.SearchGenres(query),
Studios = _libraryManager.SearchStudios(query) Studios = _libraryManager.SearchStudios(query)
}; };
return result; return result;
} }
} }
} }

View File

@ -5,34 +5,34 @@ using Kyoo.Controllers;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/shows")] [Route("api/shows")]
[Route("api/show")] [Route("api/show")]
[ApiController] [ApiController]
public class ShowsController : ControllerBase public class ShowsController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public ShowsController(ILibraryManager libraryManager) public ShowsController(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet] [HttpGet]
public IEnumerable<Show> GetShows() public IEnumerable<Show> GetShows()
{ {
return _libraryManager.GetShows(); return _libraryManager.GetShows();
} }
[HttpGet("{slug}")] [HttpGet("{slug}")]
[JsonDetailed] [JsonDetailed]
public ActionResult<Show> GetShow(string slug) public ActionResult<Show> GetShow(string slug)
{ {
Show show = _libraryManager.GetShowBySlug(slug); Show show = _libraryManager.GetShowBySlug(slug);
if (show == null) if (show == null)
return NotFound(); return NotFound();
return show; return show;
} }
} }
} }

View File

@ -7,171 +7,171 @@ using Kyoo.Controllers;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("[controller]")] [Route("[controller]")]
[ApiController] [ApiController]
public class SubtitleController : ControllerBase public class SubtitleController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ITranscoder _transcoder; private readonly ITranscoder _transcoder;
public SubtitleController(ILibraryManager libraryManager, ITranscoder transcoder) public SubtitleController(ILibraryManager libraryManager, ITranscoder transcoder)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_transcoder = transcoder; _transcoder = transcoder;
} }
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}.{identifier}.{extension?}")] [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}.{identifier}.{extension?}")]
public IActionResult GetSubtitle(string showSlug, int seasonNumber, int episodeNumber, string identifier, string extension) public IActionResult GetSubtitle(string showSlug, int seasonNumber, int episodeNumber, string identifier, string extension)
{ {
string languageTag = identifier.Substring(0, 3); string languageTag = identifier.Substring(0, 3);
bool forced = identifier.Length > 3 && identifier.Substring(4) == "forced"; bool forced = identifier.Length > 3 && identifier.Substring(4) == "forced";
Track subtitle = _libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag, forced); Track subtitle = _libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag, forced);
if (subtitle == null) if (subtitle == null)
return NotFound(); return NotFound();
if (subtitle.Codec == "subrip" && extension == "vtt") //The request wants a WebVTT from a Subrip subtitle, convert it on the fly and send it. if (subtitle.Codec == "subrip" && extension == "vtt") //The request wants a WebVTT from a Subrip subtitle, convert it on the fly and send it.
{ {
return new ConvertSubripToVtt(subtitle.Path); return new ConvertSubripToVtt(subtitle.Path);
} }
string mime; string mime;
if (subtitle.Codec == "ass") if (subtitle.Codec == "ass")
mime = "text/x-ssa"; mime = "text/x-ssa";
else else
mime = "application/x-subrip"; mime = "application/x-subrip";
//Should use appropriate mime type here //Should use appropriate mime type here
return PhysicalFile(subtitle.Path, mime); return PhysicalFile(subtitle.Path, mime);
} }
[HttpGet("extract/{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("extract/{showSlug}-s{seasonNumber}e{episodeNumber}")]
public async Task<string> ExtractSubtitle(string showSlug, long seasonNumber, long episodeNumber) public async Task<string> ExtractSubtitle(string showSlug, long seasonNumber, long episodeNumber)
{ {
Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
_libraryManager.ClearSubtitles(episode.ID); _libraryManager.ClearSubtitles(episode.ID);
Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path); Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path);
foreach (Track track in tracks) foreach (Track track in tracks)
{ {
track.EpisodeID = episode.ID; track.EpisodeID = episode.ID;
_libraryManager.RegisterTrack(track); _libraryManager.RegisterTrack(track);
} }
return "Done. " + tracks.Length + " track(s) extracted."; return "Done. " + tracks.Length + " track(s) extracted.";
} }
[HttpGet("extract/{showSlug}")] [HttpGet("extract/{showSlug}")]
public async Task<string> ExtractSubtitle(string showSlug) public async Task<string> ExtractSubtitle(string showSlug)
{ {
IEnumerable<Episode> episodes = _libraryManager.GetEpisodes(showSlug); IEnumerable<Episode> episodes = _libraryManager.GetEpisodes(showSlug);
foreach (Episode episode in episodes) foreach (Episode episode in episodes)
{ {
_libraryManager.ClearSubtitles(episode.ID); _libraryManager.ClearSubtitles(episode.ID);
Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path); Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path);
foreach (Track track in tracks) foreach (Track track in tracks)
{ {
track.EpisodeID = episode.ID; track.EpisodeID = episode.ID;
_libraryManager.RegisterTrack(track); _libraryManager.RegisterTrack(track);
} }
} }
return "Done."; return "Done.";
} }
} }
public class ConvertSubripToVtt : IActionResult public class ConvertSubripToVtt : IActionResult
{ {
private readonly string _path; private readonly string _path;
public ConvertSubripToVtt(string subtitlePath) public ConvertSubripToVtt(string subtitlePath)
{ {
_path = subtitlePath; _path = subtitlePath;
} }
public async Task ExecuteResultAsync(ActionContext context) public async Task ExecuteResultAsync(ActionContext context)
{ {
string line; string line;
List<string> lines = new List<string>(); List<string> lines = new List<string>();
context.HttpContext.Response.StatusCode = 200; context.HttpContext.Response.StatusCode = 200;
context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt"); context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt");
await using (StreamWriter writer = new StreamWriter(context.HttpContext.Response.Body)) await using (StreamWriter writer = new StreamWriter(context.HttpContext.Response.Body))
{ {
await writer.WriteLineAsync("WEBVTT"); await writer.WriteLineAsync("WEBVTT");
await writer.WriteLineAsync(""); await writer.WriteLineAsync("");
await writer.WriteLineAsync(""); await writer.WriteLineAsync("");
using (StreamReader reader = new StreamReader(_path)) using (StreamReader reader = new StreamReader(_path))
{ {
while ((line = await reader.ReadLineAsync()) != null) while ((line = await reader.ReadLineAsync()) != null)
{ {
if (line == "") if (line == "")
{ {
lines.Add(""); lines.Add("");
List<string> processedBlock = ConvertBlock(lines); List<string> processedBlock = ConvertBlock(lines);
for (int i = 0; i < processedBlock.Count; i++) for (int i = 0; i < processedBlock.Count; i++)
await writer.WriteLineAsync(processedBlock[i]); await writer.WriteLineAsync(processedBlock[i]);
lines.Clear(); lines.Clear();
} }
else else
lines.Add(line); lines.Add(line);
} }
} }
} }
await context.HttpContext.Response.Body.FlushAsync(); await context.HttpContext.Response.Body.FlushAsync();
} }
private static List<string> ConvertBlock(List<string> lines) private static List<string> ConvertBlock(List<string> lines)
{ {
lines[1] = lines[1].Replace(',', '.'); lines[1] = lines[1].Replace(',', '.');
if (lines[2].Length > 5) if (lines[2].Length > 5)
{ {
switch (lines[2].Substring(0, 6)) switch (lines[2].Substring(0, 6))
{ {
case "{\\an1}": case "{\\an1}":
lines[1] += " line:93% position:15%"; lines[1] += " line:93% position:15%";
break; break;
case "{\\an2}": case "{\\an2}":
lines[1] += " line:93%"; lines[1] += " line:93%";
break; break;
case "{\\an3}": case "{\\an3}":
lines[1] += " line:93% position:85%"; lines[1] += " line:93% position:85%";
break; break;
case "{\\an4}": case "{\\an4}":
lines[1] += " line:50% position:15%"; lines[1] += " line:50% position:15%";
break; break;
case "{\\an5}": case "{\\an5}":
lines[1] += " line:50%"; lines[1] += " line:50%";
break; break;
case "{\\an6}": case "{\\an6}":
lines[1] += " line:50% position:85%"; lines[1] += " line:50% position:85%";
break; break;
case "{\\an7}": case "{\\an7}":
lines[1] += " line:7% position:15%"; lines[1] += " line:7% position:15%";
break; break;
case "{\\an8}": case "{\\an8}":
lines[1] += " line:7%"; lines[1] += " line:7%";
break; break;
case "{\\an9}": case "{\\an9}":
lines[1] += " line:7% position:85%"; lines[1] += " line:7% position:85%";
break; break;
default: default:
lines[1] += " line:93%"; lines[1] += " line:93%";
break; break;
} }
} }
if (lines[2].StartsWith("{\\an")) if (lines[2].StartsWith("{\\an"))
lines[2] = lines[2].Substring(6); lines[2] = lines[2].Substring(6);
return lines; return lines;
} }
} }
} }

View File

@ -5,82 +5,82 @@ using Kyoo.Controllers;
namespace Kyoo.Api namespace Kyoo.Api
{ {
public class ThumbnailController : ControllerBase public class ThumbnailController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly string _peoplePath; private readonly string _peoplePath;
public ThumbnailController(ILibraryManager libraryManager, IConfiguration config) public ThumbnailController(ILibraryManager libraryManager, IConfiguration config)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_peoplePath = config.GetValue<string>("peoplePath"); _peoplePath = config.GetValue<string>("peoplePath");
} }
[HttpGet("poster/{showSlug}")] [HttpGet("poster/{showSlug}")]
public IActionResult GetShowThumb(string showSlug) public IActionResult GetShowThumb(string showSlug)
{ {
string path = _libraryManager.GetShowBySlug(showSlug)?.Path; string path = _libraryManager.GetShowBySlug(showSlug)?.Path;
if (path == null) if (path == null)
return NotFound(); return NotFound();
string thumb = Path.Combine(path, "poster.jpg"); string thumb = Path.Combine(path, "poster.jpg");
if (System.IO.File.Exists(thumb)) if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg"); return new PhysicalFileResult(thumb, "image/jpg");
return NotFound(); return NotFound();
} }
[HttpGet("logo/{showSlug}")] [HttpGet("logo/{showSlug}")]
public IActionResult GetShowLogo(string showSlug) public IActionResult GetShowLogo(string showSlug)
{ {
string path = _libraryManager.GetShowBySlug(showSlug)?.Path; string path = _libraryManager.GetShowBySlug(showSlug)?.Path;
if (path == null) if (path == null)
return NotFound(); return NotFound();
string thumb = Path.Combine(path, "logo.png"); string thumb = Path.Combine(path, "logo.png");
if (System.IO.File.Exists(thumb)) if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg"); return new PhysicalFileResult(thumb, "image/jpg");
return NotFound(); return NotFound();
} }
[HttpGet("backdrop/{showSlug}")] [HttpGet("backdrop/{showSlug}")]
public IActionResult GetShowBackdrop(string showSlug) public IActionResult GetShowBackdrop(string showSlug)
{ {
string path = _libraryManager.GetShowBySlug(showSlug)?.Path; string path = _libraryManager.GetShowBySlug(showSlug)?.Path;
if (path == null) if (path == null)
return NotFound(); return NotFound();
string thumb = Path.Combine(path, "backdrop.jpg"); string thumb = Path.Combine(path, "backdrop.jpg");
if (System.IO.File.Exists(thumb)) if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg"); return new PhysicalFileResult(thumb, "image/jpg");
return NotFound(); return NotFound();
} }
[HttpGet("peopleimg/{peopleSlug}")] [HttpGet("peopleimg/{peopleSlug}")]
public IActionResult GetPeopleIcon(string peopleSlug) public IActionResult GetPeopleIcon(string peopleSlug)
{ {
string thumbPath = Path.Combine(_peoplePath, peopleSlug + ".jpg"); string thumbPath = Path.Combine(_peoplePath, peopleSlug + ".jpg");
if (!System.IO.File.Exists(thumbPath)) if (!System.IO.File.Exists(thumbPath))
return NotFound(); return NotFound();
return new PhysicalFileResult(thumbPath, "image/jpg"); return new PhysicalFileResult(thumbPath, "image/jpg");
} }
[HttpGet("thumb/{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("thumb/{showSlug}-s{seasonNumber}e{episodeNumber}")]
public IActionResult GetEpisodeThumb(string showSlug, long seasonNumber, long episodeNumber) public IActionResult GetEpisodeThumb(string showSlug, long seasonNumber, long episodeNumber)
{ {
string path = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Path; string path = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Path;
if (path == null) if (path == null)
return NotFound(); return NotFound();
string thumb = Path.ChangeExtension(path, "jpg"); string thumb = Path.ChangeExtension(path, "jpg");
if (System.IO.File.Exists(thumb)) if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg"); return new PhysicalFileResult(thumb, "image/jpg");
return NotFound(); return NotFound();
} }
} }
} }

View File

@ -7,64 +7,64 @@ using System.Threading.Tasks;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("[controller]")] [Route("[controller]")]
[ApiController] [ApiController]
public class VideoController : ControllerBase public class VideoController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ITranscoder _transcoder; private readonly ITranscoder _transcoder;
private readonly string _transmuxPath; private readonly string _transmuxPath;
public VideoController(ILibraryManager libraryManager, ITranscoder transcoder, IConfiguration config) public VideoController(ILibraryManager libraryManager, ITranscoder transcoder, IConfiguration config)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_transcoder = transcoder; _transcoder = transcoder;
_transmuxPath = config.GetValue<string>("transmuxTempPath"); _transmuxPath = config.GetValue<string>("transmuxTempPath");
} }
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
public IActionResult Index(string showSlug, long seasonNumber, long episodeNumber) public IActionResult Index(string showSlug, long seasonNumber, long episodeNumber)
{ {
WatchItem episode = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber); WatchItem episode = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (episode != null && System.IO.File.Exists(episode.Path)) if (episode != null && System.IO.File.Exists(episode.Path))
return PhysicalFile(episode.Path, "video/x-matroska", true); return PhysicalFile(episode.Path, "video/x-matroska", true);
return NotFound(); return NotFound();
} }
[HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}")]
public async Task<IActionResult> Transmux(string showSlug, long seasonNumber, long episodeNumber) public async Task<IActionResult> Transmux(string showSlug, long seasonNumber, long episodeNumber)
{ {
WatchItem episode = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber); WatchItem episode = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (episode == null || !System.IO.File.Exists(episode.Path)) if (episode == null || !System.IO.File.Exists(episode.Path))
return NotFound(); return NotFound();
string path = await _transcoder.Transmux(episode); string path = await _transcoder.Transmux(episode);
if (path != null) if (path != null)
return PhysicalFile(path, "application/x-mpegURL ", true); return PhysicalFile(path, "application/x-mpegURL ", true);
return StatusCode(500); return StatusCode(500);
} }
[HttpGet("transmux/{episodeLink}/segment/{chunk}")] [HttpGet("transmux/{episodeLink}/segment/{chunk}")]
public IActionResult GetTransmuxedChunk(string episodeLink, string chunk) public IActionResult GetTransmuxedChunk(string episodeLink, string chunk)
{ {
string path = Path.Combine(_transmuxPath, episodeLink); string path = Path.Combine(_transmuxPath, episodeLink);
path = Path.Combine(path, "segments" + Path.DirectorySeparatorChar + chunk); path = Path.Combine(path, "segments" + Path.DirectorySeparatorChar + chunk);
return PhysicalFile(path, "video/MP2T"); return PhysicalFile(path, "video/MP2T");
} }
[HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")]
public async Task<IActionResult> Transcode(string showSlug, long seasonNumber, long episodeNumber) public async Task<IActionResult> Transcode(string showSlug, long seasonNumber, long episodeNumber)
{ {
WatchItem episode = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber); WatchItem episode = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (episode == null || !System.IO.File.Exists(episode.Path)) if (episode == null || !System.IO.File.Exists(episode.Path))
return NotFound(); return NotFound();
string path = await _transcoder.Transcode(episode); string path = await _transcoder.Transcode(episode);
if (path != null) if (path != null)
return PhysicalFile(path, "application/x-mpegURL ", true); return PhysicalFile(path, "application/x-mpegURL ", true);
return StatusCode(500); return StatusCode(500);
} }
} }
} }

View File

@ -4,26 +4,26 @@ using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class WatchController : ControllerBase public class WatchController : ControllerBase
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
public WatchController(ILibraryManager libraryManager) public WatchController(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
public ActionResult<WatchItem> Index(string showSlug, long seasonNumber, long episodeNumber) public ActionResult<WatchItem> Index(string showSlug, long seasonNumber, long episodeNumber)
{ {
WatchItem item = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber); WatchItem item = _libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if(item == null) if(item == null)
return NotFound(); return NotFound();
return item; return item;
} }
} }
} }

View File

@ -7,18 +7,18 @@ using Microsoft.Extensions.DependencyInjection;
namespace Kyoo namespace Kyoo
{ {
public class Program public class Program
{ {
public static async Task Main(string[] args) public static async Task Main(string[] args)
{ {
Console.WriteLine($"Running as: {Environment.UserName}"); Console.WriteLine($"Running as: {Environment.UserName}");
await CreateWebHostBuilder(args).Build().RunAsync(); await CreateWebHostBuilder(args).Build().RunAsync();
} }
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args) WebHost.CreateDefaultBuilder(args)
.UseKestrel((config) => { config.AddServerHeader = false; }) .UseKestrel((config) => { config.AddServerHeader = false; })
.UseUrls("http://*:5000") .UseUrls("http://*:5000")
.UseStartup<Startup>(); .UseStartup<Startup>();
} }
} }

View File

@ -12,91 +12,91 @@ using Microsoft.Extensions.Hosting;
namespace Kyoo namespace Kyoo
{ {
public class Startup public class Startup
{ {
public Startup(IConfiguration configuration) public Startup(IConfiguration configuration)
{ {
Configuration = configuration; Configuration = configuration;
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
// In production, the Angular files will be served from this directory // In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration => services.AddSpaStaticFiles(configuration =>
{ {
configuration.RootPath = "ClientApp/dist"; configuration.RootPath = "ClientApp/dist";
}); });
services.AddControllers().AddNewtonsoftJson(); services.AddControllers().AddNewtonsoftJson();
services.AddHttpClient(); services.AddHttpClient();
services.AddDbContext<DatabaseContext>(options => options.UseLazyLoadingProxies() services.AddDbContext<DatabaseContext>(options => options.UseLazyLoadingProxies()
.UseSqlite(Configuration.GetConnectionString("Database"))); .UseSqlite(Configuration.GetConnectionString("Database")));
// services.AddIdentity<ApplicationUser, IdentityRole>() // services.AddIdentity<ApplicationUser, IdentityRole>()
// .AddEntityFrameworkStores() // .AddEntityFrameworkStores()
// services.AddIdentityServer(); // services.AddIdentityServer();
services.AddScoped<ILibraryManager, LibraryManager>(); services.AddScoped<ILibraryManager, LibraryManager>();
services.AddScoped<ICrawler, Crawler>(); services.AddScoped<ICrawler, Crawler>();
services.AddSingleton<ITranscoder, Transcoder>(); services.AddSingleton<ITranscoder, Transcoder>();
services.AddSingleton<IThumbnailsManager, ThumbnailsManager>(); services.AddSingleton<IThumbnailsManager, ThumbnailsManager>();
services.AddSingleton<IProviderManager, ProviderManager>(); services.AddSingleton<IProviderManager, ProviderManager>();
services.AddSingleton<IPluginManager, PluginManager>(); services.AddSingleton<IPluginManager, PluginManager>();
services.AddHostedService<StartupCode>(); services.AddHostedService<StartupCode>();
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ {
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
else else
{ {
app.UseExceptionHandler("/Error"); app.UseExceptionHandler("/Error");
app.UseHsts(); app.UseHsts();
} }
app.Use((ctx, next) => app.Use((ctx, next) =>
{ {
ctx.Response.Headers.Remove("X-Powered-By"); ctx.Response.Headers.Remove("X-Powered-By");
ctx.Response.Headers.Remove("Server"); ctx.Response.Headers.Remove("Server");
ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen"); ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen");
ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self' data: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; style-src 'self' 'unsafe-inline'"); ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self' data: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; style-src 'self' 'unsafe-inline'");
ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
ctx.Response.Headers.Add("Referrer-Policy", "no-referrer"); ctx.Response.Headers.Add("Referrer-Policy", "no-referrer");
ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null"); ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null");
ctx.Response.Headers.Add("X-Content-Type-Options", "nosniff"); ctx.Response.Headers.Add("X-Content-Type-Options", "nosniff");
return next(); return next();
}); });
//app.UseHttpsRedirection(); //app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();
if (!env.IsDevelopment()) if (!env.IsDevelopment())
app.UseSpaStaticFiles(); app.UseSpaStaticFiles();
app.UseRouting(); app.UseRouting();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllerRoute("API Route", "api/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute("API Route", "api/{controller=Home}/{action=Index}/{id?}");
}); });
app.UseSpa(spa => app.UseSpa(spa =>
{ {
spa.Options.SourcePath = "ClientApp"; spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
spa.UseAngularCliServer(npmScript: "start"); spa.UseAngularCliServer(npmScript: "start");
} }
}); });
} }
} }
} }