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
{
public interface ICrawler
{
Task StartAsync(CancellationToken cancellationToken);
}
public interface ICrawler
{
Task StartAsync(CancellationToken cancellationToken);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,55 +4,55 @@ using System.Linq;
namespace Kyoo.Models
{
public class Collection : IMergable<Collection>
{
[JsonIgnore] public long ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
public string Poster { get; set; }
public string Overview { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; }
public IEnumerable<Show> Shows;
public class Collection : IMergable<Collection>
{
[JsonIgnore] public long ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
public string Poster { get; set; }
public string Overview { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; }
public IEnumerable<Show> Shows;
public Collection() { }
public Collection() { }
public Collection(string slug, string name, string overview, string imgPrimary)
{
Slug = slug;
Name = name;
Overview = overview;
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 Collection(string slug, string name, string overview, string imgPrimary)
{
Slug = slug;
Name = name;
Overview = overview;
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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -48,9 +48,9 @@ namespace Kyoo.Controllers
if (showID == null)
return null;
return (from track in _database.Tracks where track.Episode.ShowID == showID
&& track.Episode.SeasonNumber == seasonNumber
&& track.Episode.EpisodeNumber == episodeNumber
&& track.Language == languageTag select track).FirstOrDefault();
&& track.Episode.SeasonNumber == seasonNumber
&& track.Episode.EpisodeNumber == episodeNumber
&& track.Language == languageTag select track).FirstOrDefault();
}
@ -72,7 +72,7 @@ namespace Kyoo.Controllers
where l.CollectionID == null select show).AsEnumerable().Union(
from collection in _database.Collections select collection.AsShow())
.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);
}
@ -99,7 +99,7 @@ namespace Kyoo.Controllers
{
return (from season in _database.Seasons
where season.SeasonNumber == seasonNumber
&& season.Show.Slug == showSlug
&& season.Show.Slug == showSlug
select season).FirstOrDefault();
}
@ -107,7 +107,7 @@ namespace Kyoo.Controllers
{
return (from season in _database.Seasons
where season.SeasonNumber == seasonNumber
&& season.Show.Slug == showSlug
&& season.Show.Slug == showSlug
select season).FirstOrDefault()?.Episodes.Count() ?? 0;
}
@ -119,7 +119,7 @@ namespace Kyoo.Controllers
public IEnumerable<Episode> GetEpisodes(string showSlug, long 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)
.Select(x => x.SetLink(showSlug));
}
@ -127,20 +127,20 @@ namespace Kyoo.Controllers
public IEnumerable<Episode> GetEpisodes(long showID, long seasonNumber)
{
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)
{
return (from episode in _database.Episodes where episode.EpisodeNumber == episodeNumber
&& 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)
{
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,
episode.Show.Title,
episode.Show.Slug,

View File

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

View File

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

View File

@ -22,7 +22,7 @@ namespace Kyoo.Controllers
serviceScope.ServiceProvider.GetService<DatabaseContext>().Database.EnsureCreated();
// Use the next line if the database is not SQLite (SQLite doesn't support complexe migrations).
// serviceScope.ServiceProvider.GetService<DatabaseContext>().Database.Migrate();;
IPluginManager pluginManager = serviceScope.ServiceProvider.GetService<IPluginManager>();
pluginManager.ReloadPlugins();

View File

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

View File

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

View File

@ -8,88 +8,88 @@ using Kyoo.Models.Watch;
namespace Kyoo.Controllers.TranscoderLink
{
public static class TranscoderAPI
{
private const string TranscoderPath = "libtranscoder.so";
public static class TranscoderAPI
{
private const string TranscoderPath = "libtranscoder.so";
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int init();
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int init();
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int transmux(string path, string out_path, out float playableDuration);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int transcode(string path, string out_path, out float playableDuration);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int transmux(string path, string out_path, out float playableDuration);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int transcode(string path, string out_path, out float playableDuration);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_track_info(string path, out int array_length, out int track_count);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr extract_subtitles(string path, string out_path, out int array_length, out int track_count);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr get_track_info(string path, out int array_length, out int track_count);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr extract_subtitles(string path, string out_path, out int array_length, out int track_count);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern void free_streams(IntPtr stream_ptr);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern void free(IntPtr ptr);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern void free_streams(IntPtr stream_ptr);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private static extern void free(IntPtr ptr);
public static void GetTrackInfo(string path, out Track[] tracks)
{
int size = Marshal.SizeOf<Stream>();
IntPtr ptr = get_track_info(path, out int arrayLength, out int trackCount);
IntPtr streamsPtr = ptr;
public static void GetTrackInfo(string path, out Track[] tracks)
{
int size = Marshal.SizeOf<Stream>();
IntPtr ptr = get_track_info(path, out int arrayLength, out int trackCount);
IntPtr streamsPtr = ptr;
if (trackCount > 0 && ptr != IntPtr.Zero)
{
tracks = new Track[trackCount];
if (trackCount > 0 && ptr != IntPtr.Zero)
{
tracks = new Track[trackCount];
int j = 0;
for (int i = 0; i < arrayLength; i++)
{
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
if (stream.Type != StreamType.Unknow)
{
tracks[j] = new Track(stream);
j++;
}
streamsPtr += size;
}
}
else
tracks = new Track[0];
int j = 0;
for (int i = 0; i < arrayLength; i++)
{
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
if (stream.Type != StreamType.Unknow)
{
tracks[j] = new Track(stream);
j++;
}
streamsPtr += size;
}
}
else
tracks = new Track[0];
free(ptr);
Console.WriteLine($"\t{tracks.Length} tracks got at: {path}");
}
free(ptr);
Console.WriteLine($"\t{tracks.Length} tracks got at: {path}");
}
public static void ExtractSubtitles(string path, string outPath, out Track[] tracks)
{
int size = Marshal.SizeOf<Stream>();
IntPtr ptr = extract_subtitles(path, outPath, out int arrayLength, out int trackCount);
IntPtr streamsPtr = ptr;
public static void ExtractSubtitles(string path, string outPath, out Track[] tracks)
{
int size = Marshal.SizeOf<Stream>();
IntPtr ptr = extract_subtitles(path, outPath, out int arrayLength, out int trackCount);
IntPtr streamsPtr = ptr;
if (trackCount > 0 && ptr != IntPtr.Zero)
{
tracks = new Track[trackCount];
if (trackCount > 0 && ptr != IntPtr.Zero)
{
tracks = new Track[trackCount];
int j = 0;
for (int i = 0; i < arrayLength; i++)
{
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
if (stream.Type != StreamType.Unknow)
{
tracks[j] = new Track(stream);
j++;
}
streamsPtr += size;
}
}
else
tracks = new Track[0];
int j = 0;
for (int i = 0; i < arrayLength; i++)
{
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
if (stream.Type != StreamType.Unknow)
{
tracks[j] = new Track(stream);
j++;
}
streamsPtr += size;
}
}
else
tracks = new Track[0];
free(ptr);
Console.WriteLine($"\t{tracks.Count(x => x.Type == StreamType.Subtitle)} subtitles got at: {path}");
}
}
free(ptr);
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
{
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions options) : base(options) { }
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions options) : base(options) { }
public DbSet<Library> Libraries { get; set; }
public DbSet<Collection> Collections { get; set; }
public DbSet<Show> Shows { get; set; }
public DbSet<Season> Seasons { get; set; }
public DbSet<Episode> Episodes { get; set; }
public DbSet<Track> Tracks { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<People> Peoples { get; set; }
public DbSet<Studio> Studios { get; set; }
public DbSet<LibraryLink> LibraryLinks { get; set; }
public DbSet<CollectionLink> CollectionLinks { 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.
public DbSet<GenreLink> GenreLinks { get; set; }
private ValueConverter<string[], string> stringArrayConverter = new ValueConverter<string[], string>(
arr => string.Join("|", arr),
str => str.Split("|", StringSplitOptions.None));
public DbSet<Library> Libraries { get; set; }
public DbSet<Collection> Collections { get; set; }
public DbSet<Show> Shows { get; set; }
public DbSet<Season> Seasons { get; set; }
public DbSet<Episode> Episodes { get; set; }
public DbSet<Track> Tracks { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<People> Peoples { get; set; }
public DbSet<Studio> Studios { get; set; }
public DbSet<LibraryLink> LibraryLinks { get; set; }
public DbSet<CollectionLink> CollectionLinks { 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.
public DbSet<GenreLink> GenreLinks { get; set; }
private ValueConverter<string[], string> stringArrayConverter = new ValueConverter<string[], string>(
arr => string.Join("|", arr),
str => str.Split("|", StringSplitOptions.None));
private ValueComparer<string[]> stringArrayComparer = new ValueComparer<string[]>(
(l1, l2) => l1.SequenceEqual(l2),
arr => arr.Aggregate(0, (i, s) => s.GetHashCode()));
private ValueComparer<string[]> stringArrayComparer = new ValueComparer<string[]>(
(l1, l2) => l1.SequenceEqual(l2),
arr => arr.Aggregate(0, (i, s) => s.GetHashCode()));
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
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<Show>().Property(e => e.Aliases).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<Show>().Property(e => e.Aliases).HasConversion(stringArrayConverter).Metadata.SetValueComparer(stringArrayComparer);
modelBuilder.Entity<Track>()
.Property(t => t.IsDefault)
.ValueGeneratedNever();
modelBuilder.Entity<Track>()
.Property(t => t.IsForced)
.ValueGeneratedNever();
modelBuilder.Entity<People>()
.HasKey(x => x.Slug);
modelBuilder.Entity<Track>()
.Property(t => t.IsDefault)
.ValueGeneratedNever();
modelBuilder.Entity<Track>()
.Property(t => t.IsForced)
.ValueGeneratedNever();
modelBuilder.Entity<People>()
.HasKey(x => x.Slug);
modelBuilder.Entity<GenreLink>()
.HasKey(x => new {x.ShowID, x.GenreID});
modelBuilder.Entity<Show>()
.Ignore(x => x.Genres);
// modelBuilder.Entity<Genre>()
// .Ignore(x => x.Shows);
}
}
modelBuilder.Entity<GenreLink>()
.HasKey(x => new {x.ShowID, x.GenreID});
modelBuilder.Entity<Show>()
.Ignore(x => x.Genres);
// modelBuilder.Entity<Genre>()
// .Ignore(x => x.Shows);
}
}
}
public static class DbSetExtension

View File

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

View File

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

View File

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

View File

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

View File

@ -6,32 +6,32 @@ using System.Linq;
namespace Kyoo.Api
{
[Route("api/libraries")]
[ApiController]
public class LibrariesController : ControllerBase
{
private readonly ILibraryManager _libraryManager;
[Route("api/libraries")]
[ApiController]
public class LibrariesController : ControllerBase
{
private readonly ILibraryManager _libraryManager;
public LibrariesController(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
public LibrariesController(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
[HttpGet]
public IEnumerable<Library> GetLibraries()
{
return _libraryManager.GetLibraries();
}
[HttpGet]
public IEnumerable<Library> GetLibraries()
{
return _libraryManager.GetLibraries();
}
[HttpGet("{librarySlug}")]
public ActionResult<IEnumerable<Show>> GetShows(string librarySlug)
{
Library library = _libraryManager.GetLibrary(librarySlug);
[HttpGet("{librarySlug}")]
public ActionResult<IEnumerable<Show>> GetShows(string librarySlug)
{
Library library = _libraryManager.GetLibrary(librarySlug);
if (library == null)
return NotFound();
if (library == null)
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
{
[Route("api/[controller]")]
[ApiController]
public class PeopleController : ControllerBase
{
private readonly ILibraryManager _libraryManager;
[Route("api/[controller]")]
[ApiController]
public class PeopleController : ControllerBase
{
private readonly ILibraryManager _libraryManager;
public PeopleController(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
public PeopleController(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
[HttpGet("{peopleSlug}")]
public ActionResult<Collection> GetPeople(string peopleSlug)
{
People people = _libraryManager.GetPeopleBySlug(peopleSlug);
[HttpGet("{peopleSlug}")]
public ActionResult<Collection> GetPeople(string peopleSlug)
{
People people = _libraryManager.GetPeopleBySlug(peopleSlug);
if (people == null)
return NotFound();
Collection collection = new Collection(people.Slug, people.Name, null, null)
{
Shows = _libraryManager.GetShowsByPeople(people.Slug),
Poster = "peopleimg/" + people.Slug
};
return collection;
}
}
if (people == null)
return NotFound();
Collection collection = new Collection(people.Slug, people.Name, null, null)
{
Shows = _libraryManager.GetShowsByPeople(people.Slug),
Poster = "peopleimg/" + people.Slug
};
return collection;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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