Merge pull request #8 from AnonymusRaccoon/repositories

Repositories
This commit is contained in:
Zoe Roux 2020-06-07 20:01:58 +02:00
commit 6ecc031c94
56 changed files with 2026 additions and 801 deletions

View File

@ -1,58 +1,93 @@
using Kyoo.Models;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models;
namespace Kyoo.Controllers
{
public interface ILibraryManager
{
// Get by slug
Library GetLibrary(string librarySlug);
Collection GetCollection(string slug);
Show GetShow(string slug);
Season GetSeason(string showSlug, long seasonNumber);
Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber);
Episode GetMovieEpisode(string movieSlug);
Genre GetGenre(string slug);
Studio GetStudio(string slug);
People GetPeople(string slug);
ProviderID GetProvider(string name);
Task<Library> GetLibrary(string slug);
Task<Collection> GetCollection(string slug);
Task<Show> GetShow(string slug);
Task<Season> GetSeason(string showSlug, int seasonNumber);
Task<Episode> GetEpisode(string showSlug, int seasonNumber, int episodeNumber);
Task<Episode> GetMovieEpisode(string movieSlug);
Task<Track> GetTrack(int id);
Task<Track> GetTrack(int episodeID, string language, bool isForced);
Task<Genre> GetGenre(string slug);
Task<Studio> GetStudio(string slug);
Task<People> GetPeople(string slug);
// Get by relations
Task<ICollection<Season>> GetSeasons(int showID);
Task<ICollection<Season>> GetSeasons(string showSlug);
Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber);
Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber);
Task<ICollection<Episode>> GetEpisodes(int seasonID);
// Helpers
Task<Show> GetShowByPath(string path);
Task AddShowLink(int showID, int? libraryID, int? collectionID);
Task AddShowLink([NotNull] Show show, Library library, Collection collection);
// Get all
IEnumerable<Library> GetLibraries();
IEnumerable<Collection> GetCollections();
IEnumerable<Show> GetShows();
IEnumerable<Episode> GetEpisodes();
IEnumerable<Track> GetTracks();
IEnumerable<Studio> GetStudios();
IEnumerable<People> GetPeoples();
IEnumerable<Genre> GetGenres();
Task<ICollection<Library>> GetLibraries();
Task<ICollection<Collection>> GetCollections();
Task<ICollection<Show>> GetShows();
Task<ICollection<Season>> GetSeasons();
Task<ICollection<Episode>> GetEpisodes();
Task<ICollection<Track>> GetTracks();
Task<ICollection<Studio>> GetStudios();
Task<ICollection<People>> GetPeoples();
Task<ICollection<Genre>> GetGenres();
Task<ICollection<ProviderID>> GetProviders();
// Search
IEnumerable<Collection> SearchCollections(string searchQuery);
IEnumerable<Show> SearchShows(string searchQuery);
IEnumerable<Episode> SearchEpisodes(string searchQuery);
IEnumerable<Genre> SearchGenres(string searchQuery);
IEnumerable<Studio> SearchStudios(string searchQuery);
IEnumerable<People> SearchPeople(string searchQuery);
Task<ICollection<Library>> SearchLibraries(string searchQuery);
Task<ICollection<Collection>> SearchCollections(string searchQuery);
Task<ICollection<Show>> SearchShows(string searchQuery);
Task<ICollection<Season>> SearchSeasons(string searchQuery);
Task<ICollection<Episode>> SearchEpisodes(string searchQuery);
Task<ICollection<Genre>> SearchGenres(string searchQuery);
Task<ICollection<Studio>> SearchStudios(string searchQuery);
Task<ICollection<People>> SearchPeople(string searchQuery);
// Other get helpers
Show GetShowByPath(string path);
IEnumerable<string> GetLibrariesPath();
IEnumerable<Episode> GetEpisodes(string showSlug, long seasonNumber);
//Register values
void Register(object obj);
Task Edit(object obj, bool resetOld);
void RegisterShowLinks(Library library, Collection collection, Show show);
Task SaveChanges();
Task RegisterLibrary(Library library);
Task RegisterCollection(Collection collection);
Task RegisterShow(Show show);
Task RegisterSeason(Season season);
Task RegisterEpisode(Episode episode);
Task RegisterTrack(Track track);
Task RegisterGenre(Genre genre);
Task RegisterStudio(Studio studio);
Task RegisterPeople(People people);
// Validate values
IEnumerable<MetadataID> Validate(IEnumerable<MetadataID> id);
// Edit values
Task EditLibrary(Library library, bool resetOld);
Task EditCollection(Collection collection, bool resetOld);
Task EditShow(Show show, bool resetOld);
Task EditSeason(Season season, bool resetOld);
Task EditEpisode(Episode episode, bool resetOld);
Task EditTrack(Track track, bool resetOld);
Task EditGenre(Genre genre, bool resetOld);
Task EditStudio(Studio studio, bool resetOld);
Task EditPeople(People people, bool resetOld);
// Remove values
void RemoveShow(Show show);
void RemoveSeason(Season season);
void RemoveEpisode(Episode episode);
// Delete values
Task DelteLibrary(Library library);
Task DeleteCollection(Collection collection);
Task DeleteShow(Show show);
Task DeleteSeason(Season season);
Task DeleteEpisode(Episode episode);
Task DeleteTrack(Track track);
Task DeleteGenre(Genre genre);
Task DeleteStudio(Studio studio);
Task DeletePeople(People people);
}
}

View File

@ -14,8 +14,8 @@ namespace Kyoo.Controllers
Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie);
Task<IEnumerable<PeopleLink>> GetPeople(Show show);
Task<Season> GetSeason(Show show, long seasonNumber);
Task<Season> GetSeason(Show show, int seasonNumber);
Task<Episode> GetEpisode(Show show, long seasonNumber, long episodeNumber, long absoluteNumber);
Task<Episode> GetEpisode(Show show, int seasonNumber, int episodeNumber, int absoluteNumber);
}
}

View File

@ -10,8 +10,8 @@ namespace Kyoo.Controllers
Task<Show> CompleteShow(Show show, Library library);
Task<Show> SearchShow(string showName, bool isMovie, Library library);
Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie, Library library);
Task<Season> GetSeason(Show show, long seasonNumber, Library library);
Task<Episode> GetEpisode(Show show, string episodePath, long seasonNumber, long episodeNumber, long absoluteNumber, Library library);
Task<Season> GetSeason(Show show, int seasonNumber, Library library);
Task<Episode> GetEpisode(Show show, string episodePath, int seasonNumber, int episodeNumber, int absoluteNumber, Library library);
Task<IEnumerable<PeopleLink>> GetPeople(Show show, Library library);
}
}

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models;
namespace Kyoo.Controllers
{
public interface IRepository<T>
{
Task<T> Get(int id);
Task<T> Get(string slug);
Task<ICollection<T>> Search(string query);
Task<ICollection<T>> GetAll();
Task<int> Create([NotNull] T obj);
Task<int> CreateIfNotExists([NotNull] T obj);
Task Edit([NotNull] T edited, bool resetOld);
Task Delete(T obj);
}
public interface IShowRepository : IRepository<Show>
{
Task<Show> GetByPath(string path);
Task AddShowLink(int showID, int? libraryID, int? collectionID);
}
public interface ISeasonRepository : IRepository<Season>
{
Task<Season> Get(string showSlug, int seasonNumber);
Task<ICollection<Season>> GetSeasons(int showID);
Task<ICollection<Season>> GetSeasons(string showSlug);
}
public interface IEpisodeRepository : IRepository<Episode>
{
Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber);
Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber);
Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber);
Task<ICollection<Episode>> GetEpisodes(int seasonID);
}
public interface ITrackRepository : IRepository<Track>
{
Task<Track> Get(int episodeID, string languageTag, bool isForced);
}
public interface ILibraryRepository : IRepository<Library> {}
public interface ICollectionRepository : IRepository<Collection> {}
public interface IGenreRepository : IRepository<Genre> {}
public interface IStudioRepository : IRepository<Studio> {}
public interface IPeopleRepository : IRepository<People> {}
public interface IProviderRepository : IRepository<ProviderID> {}
}

View File

@ -11,7 +11,7 @@
<Company>SDG</Company>
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageVersion>1.0.20</PackageVersion>
<PackageVersion>1.0.21</PackageVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -7,7 +7,7 @@ namespace Kyoo.Models
{
public class Collection
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public int ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
public string Poster { get; set; }

View File

@ -2,10 +2,10 @@ namespace Kyoo.Models
{
public class CollectionLink
{
public long ID { get; set; }
public long? CollectionID { get; set; }
public int ID { get; set; }
public int? CollectionID { get; set; }
public virtual Collection Collection { get; set; }
public long ShowID { get; set; }
public int ShowID { get; set; }
public virtual Show Show { get; set; }
public CollectionLink() { }

View File

@ -6,21 +6,21 @@ namespace Kyoo.Models
{
public class Episode
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long ShowID { get; set; }
[JsonIgnore] public int ID { get; set; }
[JsonIgnore] public int ShowID { get; set; }
[JsonIgnore] public virtual Show Show { get; set; }
[JsonIgnore] public long? SeasonID { get; set; }
[JsonIgnore] public int? 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; }
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
public int 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 int Runtime { get; set; } //This runtime variable should be in minutes
[JsonIgnore] public string ImgPrimary { get; set; }
public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
@ -42,13 +42,13 @@ namespace Kyoo.Models
public Episode() { }
public Episode(long seasonNumber,
long episodeNumber,
long absoluteNumber,
public Episode(int seasonNumber,
int episodeNumber,
int absoluteNumber,
string title,
string overview,
DateTime? releaseDate,
long runtime,
int runtime,
string imgPrimary,
IEnumerable<MetadataID> externalIDs)
{
@ -63,16 +63,16 @@ namespace Kyoo.Models
ExternalIDs = externalIDs;
}
public Episode(long showID,
long seasonID,
long seasonNumber,
long episodeNumber,
long absoluteNumber,
public Episode(int showID,
int seasonID,
int seasonNumber,
int episodeNumber,
int absoluteNumber,
string path,
string title,
string overview,
DateTime? releaseDate,
long runtime,
int runtime,
string imgPrimary,
IEnumerable<MetadataID> externalIDs)
{
@ -90,7 +90,7 @@ namespace Kyoo.Models
ExternalIDs = externalIDs;
}
public static string GetSlug(string showSlug, long seasonNumber, long episodeNumber)
public static string GetSlug(string showSlug, int seasonNumber, int episodeNumber)
{
return showSlug + "-s" + seasonNumber + "e" + episodeNumber;
}

View File

@ -4,7 +4,7 @@ namespace Kyoo.Models
{
public class Genre
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public int ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
@ -24,7 +24,7 @@ namespace Kyoo.Models
Name = name;
}
public Genre(long id, string slug, string name)
public Genre(int id, string slug, string name)
{
ID = id;
Slug = slug;

View File

@ -2,9 +2,9 @@ namespace Kyoo.Models
{
public class GenreLink
{
public long ShowID { get; set; }
public int ShowID { get; set; }
public virtual Show Show { get; set; }
public long GenreID { get; set; }
public int GenreID { get; set; }
public virtual Genre Genre { get; set; }
public GenreLink() {}

View File

@ -1,5 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models.Attributes;
using Newtonsoft.Json;
@ -8,7 +7,7 @@ namespace Kyoo.Models
{
public class Library
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public int ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
public IEnumerable<string> Paths { get; set; }

View File

@ -2,12 +2,12 @@ namespace Kyoo.Models
{
public class LibraryLink
{
public long ID { get; set; }
public long LibraryID { get; set; }
public int ID { get; set; }
public int LibraryID { get; set; }
public virtual Library Library { get; set; }
public long? ShowID { get; set; }
public int? ShowID { get; set; }
public virtual Show Show { get; set; }
public long? CollectionID { get; set; }
public int? CollectionID { get; set; }
public virtual Collection Collection { get; set; }
public LibraryLink() { }

View File

@ -4,20 +4,20 @@ namespace Kyoo.Models
{
public class MetadataID
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long ProviderID { get; set; }
[JsonIgnore] public int ID { get; set; }
[JsonIgnore] public int ProviderID { get; set; }
public virtual ProviderID Provider {get; set; }
[JsonIgnore] public long? ShowID { get; set; }
[JsonIgnore] public int? ShowID { get; set; }
[JsonIgnore] public virtual Show Show { get; set; }
[JsonIgnore] public long? EpisodeID { get; set; }
[JsonIgnore] public int? EpisodeID { get; set; }
[JsonIgnore] public virtual Episode Episode { get; set; }
[JsonIgnore] public long? SeasonID { get; set; }
[JsonIgnore] public int? SeasonID { get; set; }
[JsonIgnore] public virtual Season Season { get; set; }
[JsonIgnore] public long? PeopleID { get; set; }
[JsonIgnore] public int? PeopleID { get; set; }
[JsonIgnore] public virtual People People { get; set; }
public string DataID { get; set; }

View File

@ -6,7 +6,7 @@ namespace Kyoo.Models
{
public class People
{
public long ID { get; set; }
public int ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; }

View File

@ -5,8 +5,8 @@ namespace Kyoo.Models
{
public class PeopleLink
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long PeopleID { get; set; }
[JsonIgnore] public int ID { get; set; }
[JsonIgnore] public int PeopleID { get; set; }
[JsonIgnore] public virtual People People { get; set; }
public string Slug
@ -27,7 +27,7 @@ namespace Kyoo.Models
set => People.ExternalIDs = value;
}
[JsonIgnore] public long ShowID { get; set; }
[JsonIgnore] public int ShowID { get; set; }
[JsonIgnore] public virtual Show Show { get; set; }
public string Role { get; set; }
public string Type { get; set; }

View File

@ -4,13 +4,13 @@ namespace Kyoo.Models
{
public class ProviderID
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public int ID { get; set; }
public string Name { get; set; }
public string Logo { get; set; }
public ProviderID() { }
public ProviderID(long id, string name, string logo)
public ProviderID(int id, string name, string logo)
{
ID = id;
Name = name;

View File

@ -4,10 +4,10 @@ namespace Kyoo.Models
{
public class ProviderLink
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long ProviderID { get; set; }
[JsonIgnore] public int ID { get; set; }
[JsonIgnore] public int ProviderID { get; set; }
[JsonIgnore] public virtual ProviderID Provider { get; set; }
[JsonIgnore] public long? LibraryID { get; set; }
[JsonIgnore] public int? LibraryID { get; set; }
[JsonIgnore] public virtual Library Library { get; set; }
public ProviderLink() { }

View File

@ -5,15 +5,15 @@ namespace Kyoo.Models
{
public class Season
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long ShowID { get; set; }
[JsonIgnore] public int ID { get; set; }
[JsonIgnore] public int ShowID { get; set; }
public long SeasonNumber { get; set; } = -1;
public int SeasonNumber { get; set; } = -1;
public string Slug => $"{Show.Title}-s{SeasonNumber}";
public string Slug => $"{Show.Slug}-s{SeasonNumber}";
public string Title { get; set; }
public string Overview { get; set; }
public long? Year { get; set; }
public int? Year { get; set; }
[JsonIgnore] public string ImgPrimary { get; set; }
public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
@ -23,11 +23,11 @@ namespace Kyoo.Models
public Season() { }
public Season(long showID,
long seasonNumber,
public Season(int showID,
int seasonNumber,
string title,
string overview,
long? year,
int? year,
string imgPrimary,
IEnumerable<MetadataID> externalIDs)
{

View File

@ -7,7 +7,7 @@ namespace Kyoo.Models
{
public class Show : IOnMerge
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public int ID { get; set; }
public string Slug { get; set; }
public string Title { get; set; }
@ -17,8 +17,8 @@ namespace Kyoo.Models
public Status? Status { get; set; }
public string TrailerUrl { get; set; }
public long? StartYear { get; set; }
public long? EndYear { get; set; }
public int? StartYear { get; set; }
public int? EndYear { get; set; }
public string Poster { get; set; }
public string Logo { get; set; }
@ -36,6 +36,7 @@ namespace Kyoo.Models
set => GenreLinks = value?.Select(x => new GenreLink(this, x)).ToList();
}
[NotMergable] [JsonIgnore] public virtual IEnumerable<GenreLink> GenreLinks { get; set; }
[JsonIgnore] public int? StudioID { get; set; }
public virtual Studio Studio { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleLink> People { get; set; }
[JsonIgnore] public virtual IEnumerable<Season> Seasons { get; set; }
@ -50,8 +51,8 @@ namespace Kyoo.Models
string trailerUrl,
IEnumerable<Genre> genres,
Status? status,
long? startYear,
long? endYear,
int? startYear,
int? endYear,
IEnumerable<MetadataID> externalIDs)
{
Slug = slug;
@ -75,8 +76,8 @@ namespace Kyoo.Models
string overview,
string trailerUrl,
Status? status,
long? startYear,
long? endYear,
int? startYear,
int? endYear,
string poster,
string logo,
string backdrop,

View File

@ -5,7 +5,7 @@ namespace Kyoo.Models
{
public class Studio
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public int ID { get; set; }
public string Slug { get; set; }
public string Name { get; set; }

View File

@ -14,7 +14,7 @@ namespace Kyoo.Models
public bool RunOnStartup { get; }
public int Priority { get; }
public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null);
public IEnumerable<string> GetPossibleParameters();
public Task<IEnumerable<string>> GetPossibleParameters();
public int? Progress();
}
}

View File

@ -55,8 +55,8 @@ namespace Kyoo.Models
public class Track : Stream
{
[JsonIgnore] public long ID { get; set; }
[JsonIgnore] public long EpisodeID { get; set; }
[JsonIgnore] public int ID { get; set; }
[JsonIgnore] public int EpisodeID { get; set; }
public bool IsDefault
{
get => isDefault;

View File

@ -7,12 +7,12 @@ namespace Kyoo.Models
{
public class WatchItem
{
[JsonIgnore] public readonly long EpisodeID = -1;
[JsonIgnore] public readonly int EpisodeID = -1;
public string ShowTitle;
public string ShowSlug;
public long SeasonNumber;
public long EpisodeNumber;
public int SeasonNumber;
public int EpisodeNumber;
public string Title;
public string Link;
public DateTime? ReleaseDate;
@ -28,11 +28,11 @@ namespace Kyoo.Models
public WatchItem() { }
public WatchItem(long episodeID,
public WatchItem(int episodeID,
string showTitle,
string showSlug,
long seasonNumber,
long episodeNumber,
int seasonNumber,
int episodeNumber,
string title,
DateTime? releaseDate,
string path)
@ -50,11 +50,11 @@ namespace Kyoo.Models
Link = Episode.GetSlug(ShowSlug, seasonNumber, episodeNumber);
}
public WatchItem(long episodeID,
public WatchItem(int episodeID,
string showTitle,
string showSlug,
long seasonNumber,
long episodeNumber,
int seasonNumber,
int episodeNumber,
string title,
DateTime? releaseDate,
string path,

View File

@ -1,394 +1,366 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace Kyoo.Controllers
{
public class LibraryManager : ILibraryManager
{
private const int MaxSaveRetry = 3;
private readonly DatabaseContext _database;
private readonly ILibraryRepository _libraries;
private readonly ICollectionRepository _collections;
private readonly IShowRepository _shows;
private readonly ISeasonRepository _seasons;
private readonly IEpisodeRepository _episodes;
private readonly ITrackRepository _tracks;
private readonly IGenreRepository _genres;
private readonly IStudioRepository _studios;
private readonly IPeopleRepository _people;
private readonly IProviderRepository _providers;
public LibraryManager(DatabaseContext database)
public LibraryManager(ILibraryRepository libraries,
ICollectionRepository collections,
IShowRepository shows,
ISeasonRepository seasons,
IEpisodeRepository episodes,
ITrackRepository tracks,
IGenreRepository genres,
IStudioRepository studios,
IProviderRepository providers,
IPeopleRepository people)
{
_database = database;
_libraries = libraries;
_collections = collections;
_shows = shows;
_seasons = seasons;
_episodes = episodes;
_tracks = tracks;
_genres = genres;
_studios = studios;
_providers = providers;
_people = people;
}
#region GetBySlug
public Library GetLibrary(string librarySlug)
public Task<Library> GetLibrary(string slug)
{
return _database.Libraries.FirstOrDefault(library => library.Slug == librarySlug);
return _libraries.Get(slug);
}
public Task<Collection> GetCollection(string slug)
{
return _collections.Get(slug);
}
public Task<Show> GetShow(string slug)
{
return _shows.Get(slug);
}
public Task<Season> GetSeason(string showSlug, int seasonNumber)
{
return _seasons.Get(showSlug, seasonNumber);
}
public Task<Episode> GetEpisode(string showSlug, int seasonNumber, int episodeNumber)
{
return _episodes.Get(showSlug, seasonNumber, episodeNumber);
}
public Task<Episode> GetMovieEpisode(string movieSlug)
{
return _episodes.Get(movieSlug);
}
public Task<Track> GetTrack(int id)
{
return _tracks.Get(id);
}
public Collection GetCollection(string slug)
public Task<Track> GetTrack(int episodeID, string language, bool isForced)
{
return _database.Collections.FirstOrDefault(col => col.Slug == slug);
return _tracks.Get(episodeID, language, isForced);
}
public Show GetShow(string slug)
public Task<Genre> GetGenre(string slug)
{
return _database.Shows.FirstOrDefault(show => show.Slug == slug);
return _genres.Get(slug);
}
public Season GetSeason(string showSlug, long seasonNumber)
public Task<Studio> GetStudio(string slug)
{
return _database.Seasons.FirstOrDefault(x => x.Show.Slug == showSlug && x.SeasonNumber == seasonNumber);
return _studios.Get(slug);
}
public Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber)
public Task<People> GetPeople(string slug)
{
return _database.Episodes.FirstOrDefault(x => x.EpisodeNumber == episodeNumber
&& x.SeasonNumber == seasonNumber
&& x.Show.Slug == showSlug);
return _people.Get(slug);
}
public Task<ICollection<Library>> GetLibraries()
{
return _libraries.GetAll();
}
public Task<ICollection<Collection>> GetCollections()
{
return _collections.GetAll();
}
public Task<ICollection<Show>> GetShows()
{
return _shows.GetAll();
}
public Task<ICollection<Season>> GetSeasons()
{
return _seasons.GetAll();
}
public Task<ICollection<Episode>> GetEpisodes()
{
return _episodes.GetAll();
}
public Task<ICollection<Track>> GetTracks()
{
return _tracks.GetAll();
}
public Task<ICollection<Studio>> GetStudios()
{
return _studios.GetAll();
}
public Task<ICollection<People>> GetPeoples()
{
return _people.GetAll();
}
public Task<ICollection<Genre>> GetGenres()
{
return _genres.GetAll();
}
public Task<ICollection<ProviderID>> GetProviders()
{
return _providers.GetAll();
}
public Task<ICollection<Season>> GetSeasons(int showID)
{
return _seasons.GetSeasons(showID);
}
public Task<ICollection<Season>> GetSeasons(string showSlug)
{
return _seasons.GetSeasons(showSlug);
}
public Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber)
{
return _episodes.GetEpisodes(showID, seasonNumber);
}
public Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber)
{
return _episodes.GetEpisodes(showSlug, seasonNumber);
}
public Task<ICollection<Episode>> GetEpisodes(int seasonID)
{
return _episodes.GetEpisodes(seasonID);
}
public Task<Show> GetShowByPath(string path)
{
return _shows.GetByPath(path);
}
public Task AddShowLink(int showID, int? libraryID, int? collectionID)
{
return _shows.AddShowLink(showID, libraryID, collectionID);
}
public Task AddShowLink(Show show, Library library, Collection collection)
{
if (show == null)
throw new ArgumentNullException(nameof(show));
return AddShowLink(show.ID, library?.ID, collection?.ID);
}
public Episode GetMovieEpisode(string movieSlug)
public Task<ICollection<Library>> SearchLibraries(string searchQuery)
{
return _database.Episodes.FirstOrDefault(x => x.Show.Slug == movieSlug);
return _libraries.Search(searchQuery);
}
public Genre GetGenre(string slug)
public Task<ICollection<Collection>> SearchCollections(string searchQuery)
{
return _database.Genres.FirstOrDefault(genre => genre.Slug == slug);
return _collections.Search(searchQuery);
}
public Studio GetStudio(string slug)
public Task<ICollection<Show>> SearchShows(string searchQuery)
{
return _database.Studios.FirstOrDefault(studio => studio.Slug == slug);
return _shows.Search(searchQuery);
}
public People GetPeople(string slug)
public Task<ICollection<Season>> SearchSeasons(string searchQuery)
{
return _database.Peoples.FirstOrDefault(people => people.Slug == slug);
return _seasons.Search(searchQuery);
}
public ProviderID GetProvider(string name)
public Task<ICollection<Episode>> SearchEpisodes(string searchQuery)
{
return _database.Providers.FirstOrDefault(x => x.Name == name);
return _episodes.Search(searchQuery);
}
#endregion
#region GetAll
public IEnumerable<Library> GetLibraries()
public Task<ICollection<Genre>> SearchGenres(string searchQuery)
{
return _database.Libraries;
return _genres.Search(searchQuery);
}
public IEnumerable<Collection> GetCollections()
public Task<ICollection<Studio>> SearchStudios(string searchQuery)
{
return _database.Collections;
return _studios.Search(searchQuery);
}
public IEnumerable<Show> GetShows()
public Task<ICollection<People>> SearchPeople(string searchQuery)
{
return _database.Shows;
return _people.Search(searchQuery);
}
public IEnumerable<Episode> GetEpisodes()
public Task RegisterLibrary(Library library)
{
return _database.Episodes;
}
public IEnumerable<Genre> GetGenres()
{
return _database.Genres;
}
public IEnumerable<Studio> GetStudios()
{
return _database.Studios;
return _libraries.Create(library);
}
public IEnumerable<People> GetPeoples()
public Task RegisterCollection(Collection collection)
{
return _database.Peoples;
return _collections.Create(collection);
}
public IEnumerable<Track> GetTracks()
public Task RegisterShow(Show show)
{
return _database.Tracks;
return _shows.Create(show);
}
#endregion
#region GetHelper
public IEnumerable<string> GetLibrariesPath()
public Task RegisterSeason(Season season)
{
IEnumerable<string> paths = new List<string>();
return Enumerable.Aggregate(_database.Libraries, paths, (current, lib) => current.Concat(lib.Paths));
}
public Show GetShowByPath(string path)
{
return _database.Shows.FirstOrDefault(show => show.Path == path);
return _seasons.Create(season);
}
public IEnumerable<Episode> GetEpisodes(string showSlug, long seasonNumber)
public Task RegisterEpisode(Episode episode)
{
return _database.Episodes.Where(x => x.Show.Slug == showSlug && x.SeasonNumber == seasonNumber);
}
#endregion
#region Search
public IEnumerable<Collection> SearchCollections(string searchQuery)
{
return _database.Collections.Where(collection => EF.Functions.Like(collection.Name, $"%{searchQuery}%"))
.Take(20);
}
public IEnumerable<Show> SearchShows(string searchQuery)
{
return _database.Shows.FromSqlInterpolated($@"SELECT * FROM Shows WHERE Shows.Title LIKE {$"%{searchQuery}%"}
OR Shows.Aliases LIKE {$"%{searchQuery}%"}").Take(20);
}
public IEnumerable<Episode> SearchEpisodes(string searchQuery)
{
return _database.Episodes.Where(x => EF.Functions.Like(x.Title, $"%{searchQuery}%")).Take(20);
return _episodes.Create(episode);
}
public IEnumerable<Genre> SearchGenres(string searchQuery)
public Task RegisterTrack(Track track)
{
return _database.Genres.Where(genre => EF.Functions.Like(genre.Name, $"%{searchQuery}%"))
.Take(20);
}
public IEnumerable<Studio> SearchStudios(string searchQuery)
{
return _database.Studios.Where(studio => EF.Functions.Like(studio.Name, $"%{searchQuery}%"))
.Take(20);
}
public IEnumerable<People> SearchPeople(string searchQuery)
{
return _database.Peoples.Where(people => EF.Functions.Like(people.Name, $"%{searchQuery}%"))
.OrderBy(x => x.ImgPrimary == null)
.ThenBy(x => x.Name)
.Take(20);
}
#endregion
#region Register
public void Register(object obj)
{
if (obj == null)
return;
ValidateRootEntry(_database.Entry(obj), entry =>
{
if (entry.State != EntityState.Detached)
return false;
entry.State = EntityState.Added;
return true;
});
return _tracks.Create(track);
}
public void RegisterShowLinks(Library library, Collection collection, Show show)
public Task RegisterGenre(Genre genre)
{
if (collection != null)
{
_database.LibraryLinks.AddIfNotExist(new LibraryLink {Library = library, Collection = collection},
x => x.Library == library && x.Collection == collection && x.ShowID == null);
_database.CollectionLinks.AddIfNotExist(new CollectionLink { Collection = collection, Show = show},
x => x.Collection == collection && x.Show == show);
}
else
_database.LibraryLinks.AddIfNotExist(new LibraryLink {Library = library, Show = show},
x => x.Library == library && x.Collection == null && x.Show == show);
return _genres.Create(genre);
}
public Task SaveChanges()
public Task RegisterStudio(Studio studio)
{
return SaveChanges(0);
return _studios.Create(studio);
}
private async Task SaveChanges(int retryCount)
public Task RegisterPeople(People people)
{
ValidateChanges();
try
{
await _database.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (retryCount < MaxSaveRetry)
await SaveChanges(retryCount + 1);
else
throw;
}
}
public async Task Edit(object obj, bool resetOld)
{
_database.ChangeTracker.LazyLoadingEnabled = false;
_database.ChangeTracker.AutoDetectChangesEnabled = false;
try
{
object existing = FindExisting(obj);
if (existing == null)
throw new ItemNotFound($"No existing object (of type {obj.GetType().Name}) found on the databse.");
if (resetOld)
Utility.Nullify(existing);
Utility.Merge(existing, obj);
ValidateRootEntry(_database.Entry(existing), entry => entry.State != EntityState.Added);
_database.ChangeTracker.DetectChanges();
await _database.SaveChangesAsync();
}
finally
{
_database.ChangeTracker.LazyLoadingEnabled = true;
_database.ChangeTracker.AutoDetectChangesEnabled = true;
}
}
#endregion
#region ValidateValue
private void ValidateChanges()
{
_database.ChangeTracker.AutoDetectChangesEnabled = false;
try
{
foreach (EntityEntry sourceEntry in _database.ChangeTracker.Entries())
{
if (sourceEntry.State != EntityState.Added && sourceEntry.State != EntityState.Modified)
continue;
foreach (NavigationEntry navigation in sourceEntry.Navigations)
ValidateNavigation(navigation);
}
}
finally
{
_database.ChangeTracker.AutoDetectChangesEnabled = true;
_database.ChangeTracker.DetectChanges();
}
}
private void ValidateRootEntry(EntityEntry entry, Func<EntityEntry, bool> shouldRun)
{
if (!shouldRun.Invoke(entry))
return;
foreach (NavigationEntry navigation in entry.Navigations)
{
ValidateNavigation(navigation);
if (navigation.CurrentValue == null)
continue;
if (navigation.Metadata.IsCollection())
{
IEnumerable entities = (IEnumerable)navigation.CurrentValue;
foreach (object childEntry in entities)
ValidateRootEntry(_database.Entry(childEntry), shouldRun);
}
else
ValidateRootEntry(_database.Entry(navigation.CurrentValue), shouldRun);
}
return _people.Create(people);
}
private void ValidateNavigation(NavigationEntry navigation)
public Task EditLibrary(Library library, bool resetOld)
{
object oldValue = navigation.CurrentValue;
if (oldValue == null)
return;
object newValue = Validate(oldValue);
if (ReferenceEquals(oldValue, newValue))
return;
navigation.CurrentValue = newValue;
if (!navigation.Metadata.IsCollection())
_database.Entry(oldValue).State = EntityState.Detached;
}
private T Validate<T>(T obj) where T : class
{
switch (obj)
{
case null:
return null;
case IEnumerable<object> enumerable:
return (T)Utility.RunGenericMethod(
this,
"ValidateList",
Utility.GetEnumerableType(enumerable), new [] {obj});
}
EntityState state = _database.Entry(obj).State;
if (state != EntityState.Added && state != EntityState.Detached)
return obj;
return (T)(FindExisting(obj) ?? obj);
return _libraries.Edit(library, resetOld);
}
public IEnumerable<T> ValidateList<T>(IEnumerable<T> list) where T : class
public Task EditCollection(Collection collection, bool resetOld)
{
return list.Select(x =>
{
T tmp = Validate(x);
if (tmp != x)
_database.Entry(x).State = EntityState.Detached;
return tmp ?? x;
})/*.GroupBy(GetSlug).Select(x => x.First()).Where(x => x != null)*/.ToList();
return _collections.Edit(collection, resetOld);
}
private object FindExisting(object obj)
public Task EditShow(Show show, bool resetOld)
{
return obj switch
{
Library library => GetLibrary(library.Slug),
Collection collection => GetCollection(collection.Slug),
Show show => GetShow(show.Slug),
Season season => GetSeason(season.Show.Slug, season.SeasonNumber),
Episode episode => GetEpisode(episode.Show.Slug, episode.SeasonNumber, episode.EpisodeNumber),
Studio studio => GetStudio(studio.Slug),
People people => GetPeople(people.Slug),
Genre genre => GetGenre(genre.Slug),
ProviderID provider => GetProvider(provider.Name),
_ => null
};
return _shows.Edit(show, resetOld);
}
public IEnumerable<MetadataID> Validate(IEnumerable<MetadataID> ids)
public Task EditSeason(Season season, bool resetOld)
{
return ids?.Select(x =>
{
x.Provider = _database.Providers.FirstOrDefault(y => y.Name == x.Provider.Name) ?? x.Provider;
return x;
}).GroupBy(x => x.Provider.Name).Select(x => x.First()).ToList();
}
#endregion
#region Remove
public void RemoveShow(Show show)
{
if (_database.Entry(show).State == EntityState.Detached)
_database.Shows.Attach(show);
_database.Shows.Remove(show);
return _seasons.Edit(season, resetOld);
}
public void RemoveSeason(Season season)
public Task EditEpisode(Episode episode, bool resetOld)
{
if (_database.Entry(season).State == EntityState.Detached)
_database.Seasons.Attach(season);
_database.Seasons.Remove(season);
return _episodes.Edit(episode, resetOld);
}
public void RemoveEpisode(Episode episode)
public Task EditTrack(Track track, bool resetOld)
{
if (_database.Entry(episode).State == EntityState.Detached)
_database.Episodes.Attach(episode);
_database.Episodes.Remove(episode);
return _tracks.Edit(track, resetOld);
}
public Task EditGenre(Genre genre, bool resetOld)
{
return _genres.Edit(genre, resetOld);
}
public Task EditStudio(Studio studio, bool resetOld)
{
return _studios.Edit(studio, resetOld);
}
public Task EditPeople(People people, bool resetOld)
{
return _people.Edit(people, resetOld);
}
public Task DelteLibrary(Library library)
{
return _libraries.Delete(library);
}
public Task DeleteCollection(Collection collection)
{
return _collections.Delete(collection);
}
public Task DeleteShow(Show show)
{
return _shows.Delete(show);
}
public Task DeleteSeason(Season season)
{
return _seasons.Delete(season);
}
public Task DeleteEpisode(Episode episode)
{
return _episodes.Delete(episode);
}
public Task DeleteTrack(Track track)
{
return _tracks.Delete(track);
}
public Task DeleteGenre(Genre genre)
{
return _genres.Delete(genre);
}
public Task DeleteStudio(Studio studio)
{
return _studios.Delete(studio);
}
public Task DeletePeople(People people)
{
return _people.Delete(people);
}
#endregion
}
}

View File

@ -113,13 +113,14 @@ namespace Kyoo.Controllers
});
}
public async Task<Season> GetSeason(Show show, long seasonNumber, Library library)
public async Task<Season> GetSeason(Show show, int seasonNumber, Library library)
{
Season season = await GetMetadata(
provider => provider.GetSeason(show, seasonNumber),
library,
$"the season {seasonNumber} of {show.Title}");
season.Show = show;
season.ShowID = show.ID;
season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber;
season.Title ??= $"Season {season.SeasonNumber}";
return season;
@ -127,9 +128,9 @@ namespace Kyoo.Controllers
public async Task<Episode> GetEpisode(Show show,
string episodePath,
long seasonNumber,
long episodeNumber,
long absoluteNumber,
int seasonNumber,
int episodeNumber,
int absoluteNumber,
Library library)
{
Episode episode = await GetMetadata(
@ -137,6 +138,7 @@ namespace Kyoo.Controllers
library,
"an episode");
episode.Show = show;
episode.ShowID = show.ID;
episode.Path = episodePath;
episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber;
episode.EpisodeNumber = episode.EpisodeNumber != -1 ? episode.EpisodeNumber : episodeNumber;
@ -155,6 +157,7 @@ namespace Kyoo.Controllers
.Select(x =>
{
x.Show = show;
x.ShowID = show.ID;
return x;
}).ToList();
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class CollectionRepository : ICollectionRepository
{
private readonly DatabaseContext _database;
public CollectionRepository(DatabaseContext database)
{
_database = database;
}
public Task<Collection> Get(int id)
{
return _database.Collections.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Collection> Get(string slug)
{
return _database.Collections.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<ICollection<Collection>> Search(string query)
{
return await _database.Collections
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Collection>> GetAll()
{
return await _database.Collections.ToListAsync();
}
public async Task<int> Create(Collection obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await _database.Collections.AddAsync(obj);
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(Collection obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Collection old = await Get(obj.Slug);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(Collection edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Collection old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No collection found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
}
public async Task Delete(Collection obj)
{
_database.Collections.Remove(obj);
await _database.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
{
public class EpisodeRepository : IEpisodeRepository
{
private readonly DatabaseContext _database;
private readonly IServiceProvider _serviceProvider;
public EpisodeRepository(DatabaseContext database, IServiceProvider serviceProvider)
{
_database = database;
_serviceProvider = serviceProvider;
}
public async Task<Episode> Get(int id)
{
return await _database.Episodes.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Episode> Get(string slug)
{
int sIndex = slug.IndexOf("-s", StringComparison.Ordinal);
int eIndex = slug.IndexOf("-e", StringComparison.Ordinal);
if (sIndex == -1 || eIndex == -1 || eIndex < sIndex)
throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}");
string showSlug = slug.Substring(0, sIndex);
if (!int.TryParse(slug.Substring(sIndex + 2), out int seasonNumber))
throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}");
if (!int.TryParse(slug.Substring(eIndex + 2), out int episodeNumber))
throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}");
return Get(showSlug, seasonNumber, episodeNumber);
}
public async Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
{
return await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber
&& x.EpisodeNumber == episodeNumber);
}
public async Task<ICollection<Episode>> Search(string query)
{
return await _database.Episodes
.Where(x => EF.Functions.Like(x.Title, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Episode>> GetAll()
{
return await _database.Episodes.ToListAsync();
}
public async Task<int> Create(Episode obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await Validate(obj);
_database.Entry(obj).State = EntityState.Added;
if (obj.ExternalIDs != null)
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Added;
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(Episode obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Episode old = await Get(obj.Slug);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(Episode edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Episode old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No episode found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
}
private async Task Validate(Episode obj)
{
if (obj.ShowID <= 0)
throw new InvalidOperationException($"Can't store an episode not related to any show (showID: {obj.ShowID}).");
if (obj.ExternalIDs != null)
{
obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x =>
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
x.ProviderID = await providers.CreateIfNotExists(x.Provider);
return x;
}))).ToList();
}
}
public async Task Delete(Episode obj)
{
_database.Episodes.Remove(obj);
await _database.SaveChangesAsync();
}
public async Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber)
{
return await _database.Episodes.Where(x => x.ShowID == showID
&& x.SeasonNumber == seasonNumber).ToListAsync();
}
public async Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber)
{
return await _database.Episodes.Where(x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber).ToListAsync();
}
public async Task<ICollection<Episode>> GetEpisodes(int seasonID)
{
return await _database.Episodes.Where(x => x.SeasonID == seasonID).ToListAsync();
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class GenreRepository : IGenreRepository
{
private readonly DatabaseContext _database;
public GenreRepository(DatabaseContext database)
{
_database = database;
}
public async Task<Genre> Get(int id)
{
return await _database.Genres.FirstOrDefaultAsync(x => x.ID == id);
}
public async Task<Genre> Get(string slug)
{
return await _database.Genres.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<ICollection<Genre>> Search(string query)
{
return await _database.Genres
.Where(genre => EF.Functions.Like(genre.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Genre>> GetAll()
{
return await _database.Genres.ToListAsync();
}
public async Task<int> Create(Genre obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await _database.Genres.AddAsync(obj);
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(Genre obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Genre old = await Get(obj.Slug);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(Genre edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Genre old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No genre found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
}
public async Task Delete(Genre obj)
{
_database.Genres.Remove(obj);
await _database.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
{
public class LibraryRepository : ILibraryRepository
{
private readonly DatabaseContext _database;
private readonly IServiceProvider _serviceProvider;
public LibraryRepository(DatabaseContext database, IServiceProvider serviceProvider)
{
_database = database;
_serviceProvider = serviceProvider;
}
public Task<Library> Get(int id)
{
return _database.Libraries.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Library> Get(string slug)
{
return _database.Libraries.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<ICollection<Library>> Search(string query)
{
return await _database.Libraries
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Library>> GetAll()
{
return await _database.Libraries.ToListAsync();
}
public async Task<int> Create(Library obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await Validate(obj);
_database.Entry(obj).State = EntityState.Added;
if (obj.ProviderLinks != null)
foreach (ProviderLink entry in obj.ProviderLinks)
_database.Entry(entry).State = EntityState.Added;
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(Library obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Library old = await Get(obj.Name);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(Library edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Library old = await Get(edited.Name);
if (old == null)
throw new ItemNotFound($"No library found with the name {edited.Name}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
}
private async Task Validate(Library obj)
{
obj.ProviderLinks = (await Task.WhenAll(obj.ProviderLinks.Select(async x =>
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
x.ProviderID = await providers.CreateIfNotExists(x.Provider);
return x;
}))).ToList();
}
public async Task Delete(Library obj)
{
_database.Libraries.Remove(obj);
await _database.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
{
public class PeopleRepository : IPeopleRepository
{
private readonly DatabaseContext _database;
private readonly IServiceProvider _serviceProvider;
public PeopleRepository(DatabaseContext database, IServiceProvider serviceProvider)
{
_database = database;
_serviceProvider = serviceProvider;
}
public Task<People> Get(int id)
{
return _database.Peoples.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<People> Get(string slug)
{
return _database.Peoples.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<ICollection<People>> Search(string query)
{
return await _database.Peoples
.Where(people => EF.Functions.Like(people.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<People>> GetAll()
{
return await _database.Peoples.ToListAsync();
}
public async Task<int> Create(People obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await Validate(obj);
_database.Entry(obj).State = EntityState.Added;
if (obj.ExternalIDs != null)
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Added;
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(People obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
People old = await Get(obj.Slug);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(People edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
People old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No people found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
}
private async Task Validate(People obj)
{
obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x =>
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
x.ProviderID = await providers.CreateIfNotExists(x.Provider);
return x;
}))).ToList();
}
public async Task Delete(People obj)
{
_database.Peoples.Remove(obj);
await _database.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class ProviderRepository : IProviderRepository
{
private readonly DatabaseContext _database;
public ProviderRepository(DatabaseContext database)
{
_database = database;
}
public async Task<ProviderID> Get(int id)
{
return await _database.Providers.FirstOrDefaultAsync(x => x.ID == id);
}
public async Task<ProviderID> Get(string slug)
{
return await _database.Providers.FirstOrDefaultAsync(x => x.Name == slug);
}
public async Task<ICollection<ProviderID>> Search(string query)
{
return await _database.Providers
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<ProviderID>> GetAll()
{
return await _database.Providers.ToListAsync();
}
public async Task<int> Create(ProviderID obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await _database.Providers.AddAsync(obj);
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(ProviderID obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
ProviderID old = await Get(obj.Name);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(ProviderID edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
ProviderID old = await Get(edited.Name);
if (old == null)
throw new ItemNotFound($"No provider found with the name {edited.Name}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
}
public async Task Delete(ProviderID obj)
{
_database.Providers.Remove(obj);
await _database.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
{
public class SeasonRepository : ISeasonRepository
{
private readonly DatabaseContext _database;
private readonly IServiceProvider _serviceProvider;
public SeasonRepository(DatabaseContext database, IServiceProvider serviceProvider)
{
_database = database;
_serviceProvider = serviceProvider;
}
public async Task<Season> Get(int id)
{
return await _database.Seasons.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Season> Get(string slug)
{
int index = slug.IndexOf("-s", StringComparison.Ordinal);
if (index == -1)
throw new InvalidOperationException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
string showSlug = slug.Substring(0, index);
if (!int.TryParse(slug.Substring(index + 2), out int seasonNumber))
throw new InvalidOperationException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
return Get(showSlug, seasonNumber);
}
public async Task<Season> Get(string showSlug, int seasonNumber)
{
return await _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber);
}
public async Task<ICollection<Season>> Search(string query)
{
return await _database.Seasons
.Where(x => EF.Functions.Like(x.Title, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Season>> GetAll()
{
return await _database.Seasons.ToListAsync();
}
public async Task<int> Create(Season obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await Validate(obj);
_database.Entry(obj).State = EntityState.Added;
if (obj.ExternalIDs != null)
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Added;
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(Season obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Season old = await Get(obj.Slug);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(Season edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Season old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No season found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
}
private async Task Validate(Season obj)
{
if (obj.ShowID <= 0)
throw new InvalidOperationException($"Can't store a season not related to any show (showID: {obj.ShowID}).");
if (obj.ExternalIDs != null)
{
obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x =>
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
x.ProviderID = await providers.CreateIfNotExists(x.Provider);
return x;
}))).ToList();
}
}
public async Task Delete(Season obj)
{
_database.Seasons.Remove(obj);
await _database.SaveChangesAsync();
}
public async Task<ICollection<Season>> GetSeasons(int showID)
{
return await _database.Seasons.Where(x => x.ShowID == showID).ToListAsync();
}
public async Task<ICollection<Season>> GetSeasons(string showSlug)
{
return await _database.Seasons.Where(x => x.Show.Slug == showSlug).ToListAsync();
}
}
}

View File

@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
{
public class ShowRepository : IShowRepository
{
private readonly DatabaseContext _database;
private readonly IServiceProvider _serviceProvider;
private readonly IStudioRepository _studios;
public ShowRepository(DatabaseContext database,
IServiceProvider serviceProvider,
IStudioRepository studios)
{
_database = database;
_serviceProvider = serviceProvider;
_studios = studios;
}
public async Task<Show> Get(int id)
{
return await _database.Shows.FirstOrDefaultAsync(x => x.ID == id);
}
public async Task<Show> Get(string slug)
{
return await _database.Shows.FirstOrDefaultAsync(x => x.Slug == slug);
}
public async Task<Show> GetByPath(string path)
{
return await _database.Shows.FirstOrDefaultAsync(x => x.Path == path);
}
public async Task<ICollection<Show>> Search(string query)
{
return await _database.Shows
.FromSqlInterpolated($@"SELECT * FROM Shows WHERE Shows.Title LIKE {$"%{query}%"}
OR Shows.Aliases LIKE {$"%{query}%"}")
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Show>> GetAll()
{
return await _database.Shows.ToListAsync();
}
public async Task<int> Create(Show obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await Validate(obj);
_database.Entry(obj).State = EntityState.Added;
if (obj.GenreLinks != null)
foreach (GenreLink entry in obj.GenreLinks)
_database.Entry(entry).State = EntityState.Added;
if (obj.People != null)
foreach (PeopleLink entry in obj.People)
_database.Entry(entry).State = EntityState.Added;
if (obj.ExternalIDs != null)
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Added;
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(Show obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Show old = await Get(obj.Slug);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(Show edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Show old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No show found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await _database.SaveChangesAsync();
}
private async Task Validate(Show obj)
{
if (obj.Studio != null)
obj.StudioID = await _studios.CreateIfNotExists(obj.Studio);
if (obj.GenreLinks != null)
{
obj.GenreLinks = (await Task.WhenAll(obj.GenreLinks.Select(async x =>
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IGenreRepository genres = serviceScope.ServiceProvider.GetService<IGenreRepository>();
x.GenreID = await genres.CreateIfNotExists(x.Genre);
return x;
}))).ToList();
}
if (obj.People != null)
{
obj.People = (await Task.WhenAll(obj.People.Select(async x =>
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IPeopleRepository people = serviceScope.ServiceProvider.GetService<IPeopleRepository>();
x.PeopleID = await people.CreateIfNotExists(x.People);
return x;
}))).ToList();
}
if (obj.ExternalIDs != null)
{
obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x =>
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
x.ProviderID = await providers.CreateIfNotExists(x.Provider);
return x;
}))).ToList();
}
}
public async Task Delete(Show show)
{
_database.Shows.Remove(show);
await _database.SaveChangesAsync();
}
public async Task AddShowLink(int showID, int? libraryID, int? collectionID)
{
if (collectionID != null)
{
_database.CollectionLinks.AddIfNotExist(new CollectionLink { CollectionID = collectionID, ShowID = showID},
x => x.CollectionID == collectionID && x.ShowID == showID);
}
if (libraryID != null)
{
_database.LibraryLinks.AddIfNotExist(new LibraryLink {LibraryID = libraryID.Value, ShowID = showID},
x => x.LibraryID == libraryID.Value && x.CollectionID == null && x.ShowID == showID);
}
if (libraryID != null && collectionID != null)
{
_database.LibraryLinks.AddIfNotExist(
new LibraryLink {LibraryID = libraryID.Value, CollectionID = collectionID.Value},
x => x.LibraryID == libraryID && x.CollectionID == collectionID && x.ShowID == null);
}
await _database.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class StudioRepository : IStudioRepository
{
private readonly DatabaseContext _database;
public StudioRepository(DatabaseContext database)
{
_database = database;
}
public async Task<Studio> Get(int id)
{
return await _database.Studios.FirstOrDefaultAsync(x => x.ID == id);
}
public async Task<Studio> Get(string slug)
{
return await _database.Studios.FirstOrDefaultAsync(x => x.Name == slug);
}
public async Task<ICollection<Studio>> Search(string query)
{
return await _database.Studios
.Where(x => EF.Functions.Like(x.Name, $"%{query}%"))
.Take(20)
.ToListAsync();
}
public async Task<ICollection<Studio>> GetAll()
{
return await _database.Studios.ToListAsync();
}
public async Task<int> Create(Studio obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
await _database.Studios.AddAsync(obj);
await _database.SaveChangesAsync();
return obj.ID;
}
public async Task<int> CreateIfNotExists(Studio obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
Studio old = await Get(obj.Name);
if (old != null)
return old.ID;
return await Create(obj);
}
public async Task Edit(Studio edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Studio old = await Get(edited.Name);
if (old == null)
throw new ItemNotFound($"No studio found with the name {edited.Name}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
}
public async Task Delete(Studio obj)
{
_database.Studios.Remove(obj);
await _database.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers
{
public class TrackRepository : ITrackRepository
{
private readonly DatabaseContext _database;
public TrackRepository(DatabaseContext database)
{
_database = database;
}
public async Task<Track> Get(int id)
{
return await _database.Tracks.FirstOrDefaultAsync(x => x.ID == id);
}
public Task<Track> Get(string slug)
{
throw new InvalidOperationException("Tracks do not support the get by slug method.");
}
public Task<Track> Get(int episodeID, string languageTag, bool isForced)
{
return _database.Tracks.FirstOrDefaultAsync(x => x.EpisodeID == episodeID
&& x.Language == languageTag
&& x.IsForced == isForced);
}
public Task<ICollection<Track>> Search(string query)
{
throw new InvalidOperationException("Tracks do not support the search method.");
}
public async Task<ICollection<Track>> GetAll()
{
return await _database.Tracks.ToListAsync();
}
public async Task<int> Create(Track obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
if (obj.EpisodeID <= 0)
throw new InvalidOperationException($"Can't store a track not related to any episode (episodeID: {obj.EpisodeID}).");
obj.Episode = null;
await _database.Tracks.AddAsync(obj);
await _database.SaveChangesAsync();
return obj.ID;
}
public Task<int> CreateIfNotExists(Track obj)
{
return Create(obj);
}
public async Task Edit(Track edited, bool resetOld)
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
Track old = await Get(edited.ID);
if (old == null)
throw new ItemNotFound($"No track found with the ID {edited.ID}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await _database.SaveChangesAsync();
}
public async Task Delete(Track obj)
{
_database.Tracks.Remove(obj);
await _database.SaveChangesAsync();
}
}
}

View File

@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Kyoo.Models.DatabaseMigrations.Internal
{
[DbContext(typeof(DatabaseContext))]
[Migration("20200526235513_Initial")]
[Migration("20200607010830_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -23,9 +23,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Collection", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
@ -53,16 +53,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.CollectionLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long?>("CollectionID")
.HasColumnType("bigint");
b.Property<int?>("CollectionID")
.HasColumnType("integer");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.HasKey("ID");
@ -75,16 +75,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Episode", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long>("AbsoluteNumber")
.HasColumnType("bigint");
b.Property<int>("AbsoluteNumber")
.HasColumnType("integer");
b.Property<long>("EpisodeNumber")
.HasColumnType("bigint");
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<string>("ImgPrimary")
.HasColumnType("text");
@ -98,17 +98,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<DateTime?>("ReleaseDate")
.HasColumnType("timestamp without time zone");
b.Property<long>("Runtime")
.HasColumnType("bigint");
b.Property<int>("Runtime")
.HasColumnType("integer");
b.Property<long?>("SeasonID")
.HasColumnType("bigint");
b.Property<int?>("SeasonID")
.HasColumnType("integer");
b.Property<long>("SeasonNumber")
.HasColumnType("bigint");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Title")
.HasColumnType("text");
@ -124,9 +124,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Genre", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Name")
@ -145,11 +145,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.GenreLink", b =>
{
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<long>("GenreID")
.HasColumnType("bigint");
b.Property<int>("GenreID")
.HasColumnType("integer");
b.HasKey("ShowID", "GenreID");
@ -160,9 +160,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Library", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Name")
@ -184,19 +184,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.LibraryLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long?>("CollectionID")
.HasColumnType("bigint");
b.Property<int?>("CollectionID")
.HasColumnType("integer");
b.Property<long>("LibraryID")
.HasColumnType("bigint");
b.Property<int>("LibraryID")
.HasColumnType("integer");
b.Property<long?>("ShowID")
.HasColumnType("bigint");
b.Property<int?>("ShowID")
.HasColumnType("integer");
b.HasKey("ID");
@ -211,31 +211,31 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("DataID")
.HasColumnType("text");
b.Property<long?>("EpisodeID")
.HasColumnType("bigint");
b.Property<int?>("EpisodeID")
.HasColumnType("integer");
b.Property<string>("Link")
.HasColumnType("text");
b.Property<long?>("PeopleID")
.HasColumnType("bigint");
b.Property<int?>("PeopleID")
.HasColumnType("integer");
b.Property<long>("ProviderID")
.HasColumnType("bigint");
b.Property<int>("ProviderID")
.HasColumnType("integer");
b.Property<long?>("SeasonID")
.HasColumnType("bigint");
b.Property<int?>("SeasonID")
.HasColumnType("integer");
b.Property<long?>("ShowID")
.HasColumnType("bigint");
b.Property<int?>("ShowID")
.HasColumnType("integer");
b.HasKey("ID");
@ -254,9 +254,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.People", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
@ -278,19 +278,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.PeopleLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long>("PeopleID")
.HasColumnType("bigint");
b.Property<int>("PeopleID")
.HasColumnType("integer");
b.Property<string>("Role")
.HasColumnType("text");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Type")
.HasColumnType("text");
@ -306,9 +306,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Logo")
@ -327,16 +327,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.ProviderLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long?>("LibraryID")
.HasColumnType("bigint");
b.Property<int?>("LibraryID")
.HasColumnType("integer");
b.Property<long>("ProviderID")
.HasColumnType("bigint");
b.Property<int>("ProviderID")
.HasColumnType("integer");
b.HasKey("ID");
@ -349,9 +349,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Season", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
@ -360,17 +360,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Overview")
.HasColumnType("text");
b.Property<long>("SeasonNumber")
.HasColumnType("bigint");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<long?>("Year")
.HasColumnType("bigint");
b.Property<int?>("Year")
.HasColumnType("integer");
b.HasKey("ID");
@ -381,9 +381,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Show", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Aliases")
@ -392,8 +392,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Backdrop")
.HasColumnType("text");
b.Property<long?>("EndYear")
.HasColumnType("bigint");
b.Property<int?>("EndYear")
.HasColumnType("integer");
b.Property<bool>("IsMovie")
.HasColumnType("boolean");
@ -413,14 +413,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Slug")
.HasColumnType("text");
b.Property<long?>("StartYear")
.HasColumnType("bigint");
b.Property<int?>("StartYear")
.HasColumnType("integer");
b.Property<int?>("Status")
.HasColumnType("integer");
b.Property<long?>("StudioID")
.HasColumnType("bigint");
b.Property<int?>("StudioID")
.HasColumnType("integer");
b.Property<string>("Title")
.HasColumnType("text");
@ -440,9 +440,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Studio", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Name")
@ -461,16 +461,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Track", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Codec")
.HasColumnType("text");
b.Property<long>("EpisodeID")
.HasColumnType("bigint");
b.Property<int>("EpisodeID")
.HasColumnType("integer");
b.Property<bool>("IsDefault")
.HasColumnType("boolean");

View File

@ -12,7 +12,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Collections",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Slug = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
@ -29,7 +29,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Genres",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Slug = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true)
@ -43,7 +43,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Libraries",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Slug = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
@ -58,7 +58,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Peoples",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Slug = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true),
@ -73,7 +73,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Providers",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(nullable: true),
Logo = table.Column<string>(nullable: true)
@ -87,7 +87,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Studios",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Slug = table.Column<string>(nullable: true),
Name = table.Column<string>(nullable: true)
@ -101,10 +101,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "ProviderLinks",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ProviderID = table.Column<long>(nullable: false),
LibraryID = table.Column<long>(nullable: true)
ProviderID = table.Column<int>(nullable: false),
LibraryID = table.Column<int>(nullable: true)
},
constraints: table =>
{
@ -127,7 +127,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Shows",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Slug = table.Column<string>(nullable: true),
Title = table.Column<string>(nullable: true),
@ -136,13 +136,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
Overview = table.Column<string>(nullable: true),
Status = table.Column<int>(nullable: true),
TrailerUrl = table.Column<string>(nullable: true),
StartYear = table.Column<long>(nullable: true),
EndYear = table.Column<long>(nullable: true),
StartYear = table.Column<int>(nullable: true),
EndYear = table.Column<int>(nullable: true),
Poster = table.Column<string>(nullable: true),
Logo = table.Column<string>(nullable: true),
Backdrop = table.Column<string>(nullable: true),
IsMovie = table.Column<bool>(nullable: false),
StudioID = table.Column<long>(nullable: true)
StudioID = table.Column<int>(nullable: true)
},
constraints: table =>
{
@ -159,10 +159,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "CollectionLinks",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
CollectionID = table.Column<long>(nullable: true),
ShowID = table.Column<long>(nullable: false)
CollectionID = table.Column<int>(nullable: true),
ShowID = table.Column<int>(nullable: false)
},
constraints: table =>
{
@ -185,8 +185,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "GenreLinks",
columns: table => new
{
ShowID = table.Column<long>(nullable: false),
GenreID = table.Column<long>(nullable: false)
ShowID = table.Column<int>(nullable: false),
GenreID = table.Column<int>(nullable: false)
},
constraints: table =>
{
@ -209,11 +209,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "LibraryLinks",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
LibraryID = table.Column<long>(nullable: false),
ShowID = table.Column<long>(nullable: true),
CollectionID = table.Column<long>(nullable: true)
LibraryID = table.Column<int>(nullable: false),
ShowID = table.Column<int>(nullable: true),
CollectionID = table.Column<int>(nullable: true)
},
constraints: table =>
{
@ -242,10 +242,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "PeopleLinks",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
PeopleID = table.Column<long>(nullable: false),
ShowID = table.Column<long>(nullable: false),
PeopleID = table.Column<int>(nullable: false),
ShowID = table.Column<int>(nullable: false),
Role = table.Column<string>(nullable: true),
Type = table.Column<string>(nullable: true)
},
@ -270,13 +270,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Seasons",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ShowID = table.Column<long>(nullable: false),
SeasonNumber = table.Column<long>(nullable: false),
ShowID = table.Column<int>(nullable: false),
SeasonNumber = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true),
Overview = table.Column<string>(nullable: true),
Year = table.Column<long>(nullable: true),
Year = table.Column<int>(nullable: true),
ImgPrimary = table.Column<string>(nullable: true)
},
constraints: table =>
@ -294,18 +294,18 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Episodes",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ShowID = table.Column<long>(nullable: false),
SeasonID = table.Column<long>(nullable: true),
SeasonNumber = table.Column<long>(nullable: false),
EpisodeNumber = table.Column<long>(nullable: false),
AbsoluteNumber = table.Column<long>(nullable: false),
ShowID = table.Column<int>(nullable: false),
SeasonID = table.Column<int>(nullable: true),
SeasonNumber = table.Column<int>(nullable: false),
EpisodeNumber = table.Column<int>(nullable: false),
AbsoluteNumber = table.Column<int>(nullable: false),
Path = table.Column<string>(nullable: true),
Title = table.Column<string>(nullable: true),
Overview = table.Column<string>(nullable: true),
ReleaseDate = table.Column<DateTime>(nullable: true),
Runtime = table.Column<long>(nullable: false),
Runtime = table.Column<int>(nullable: false),
ImgPrimary = table.Column<string>(nullable: true)
},
constraints: table =>
@ -329,13 +329,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "MetadataIds",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
ProviderID = table.Column<long>(nullable: false),
ShowID = table.Column<long>(nullable: true),
EpisodeID = table.Column<long>(nullable: true),
SeasonID = table.Column<long>(nullable: true),
PeopleID = table.Column<long>(nullable: true),
ProviderID = table.Column<int>(nullable: false),
ShowID = table.Column<int>(nullable: true),
EpisodeID = table.Column<int>(nullable: true),
SeasonID = table.Column<int>(nullable: true),
PeopleID = table.Column<int>(nullable: true),
DataID = table.Column<string>(nullable: true),
Link = table.Column<string>(nullable: true)
},
@ -378,14 +378,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Tracks",
columns: table => new
{
ID = table.Column<long>(nullable: false)
ID = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Title = table.Column<string>(nullable: true),
Language = table.Column<string>(nullable: true),
Codec = table.Column<string>(nullable: true),
Path = table.Column<string>(nullable: true),
Type = table.Column<int>(nullable: false),
EpisodeID = table.Column<long>(nullable: false),
EpisodeID = table.Column<int>(nullable: false),
IsDefault = table.Column<bool>(nullable: false),
IsForced = table.Column<bool>(nullable: false),
IsExternal = table.Column<bool>(nullable: false)

View File

@ -21,9 +21,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Collection", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
@ -51,16 +51,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.CollectionLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long?>("CollectionID")
.HasColumnType("bigint");
b.Property<int?>("CollectionID")
.HasColumnType("integer");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.HasKey("ID");
@ -73,16 +73,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Episode", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long>("AbsoluteNumber")
.HasColumnType("bigint");
b.Property<int>("AbsoluteNumber")
.HasColumnType("integer");
b.Property<long>("EpisodeNumber")
.HasColumnType("bigint");
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<string>("ImgPrimary")
.HasColumnType("text");
@ -96,17 +96,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<DateTime?>("ReleaseDate")
.HasColumnType("timestamp without time zone");
b.Property<long>("Runtime")
.HasColumnType("bigint");
b.Property<int>("Runtime")
.HasColumnType("integer");
b.Property<long?>("SeasonID")
.HasColumnType("bigint");
b.Property<int?>("SeasonID")
.HasColumnType("integer");
b.Property<long>("SeasonNumber")
.HasColumnType("bigint");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Title")
.HasColumnType("text");
@ -122,9 +122,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Genre", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Name")
@ -143,11 +143,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.GenreLink", b =>
{
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<long>("GenreID")
.HasColumnType("bigint");
b.Property<int>("GenreID")
.HasColumnType("integer");
b.HasKey("ShowID", "GenreID");
@ -158,9 +158,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Library", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Name")
@ -182,19 +182,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.LibraryLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long?>("CollectionID")
.HasColumnType("bigint");
b.Property<int?>("CollectionID")
.HasColumnType("integer");
b.Property<long>("LibraryID")
.HasColumnType("bigint");
b.Property<int>("LibraryID")
.HasColumnType("integer");
b.Property<long?>("ShowID")
.HasColumnType("bigint");
b.Property<int?>("ShowID")
.HasColumnType("integer");
b.HasKey("ID");
@ -209,31 +209,31 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("DataID")
.HasColumnType("text");
b.Property<long?>("EpisodeID")
.HasColumnType("bigint");
b.Property<int?>("EpisodeID")
.HasColumnType("integer");
b.Property<string>("Link")
.HasColumnType("text");
b.Property<long?>("PeopleID")
.HasColumnType("bigint");
b.Property<int?>("PeopleID")
.HasColumnType("integer");
b.Property<long>("ProviderID")
.HasColumnType("bigint");
b.Property<int>("ProviderID")
.HasColumnType("integer");
b.Property<long?>("SeasonID")
.HasColumnType("bigint");
b.Property<int?>("SeasonID")
.HasColumnType("integer");
b.Property<long?>("ShowID")
.HasColumnType("bigint");
b.Property<int?>("ShowID")
.HasColumnType("integer");
b.HasKey("ID");
@ -252,9 +252,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.People", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
@ -276,19 +276,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.PeopleLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long>("PeopleID")
.HasColumnType("bigint");
b.Property<int>("PeopleID")
.HasColumnType("integer");
b.Property<string>("Role")
.HasColumnType("text");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Type")
.HasColumnType("text");
@ -304,9 +304,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Logo")
@ -325,16 +325,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.ProviderLink", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<long?>("LibraryID")
.HasColumnType("bigint");
b.Property<int?>("LibraryID")
.HasColumnType("integer");
b.Property<long>("ProviderID")
.HasColumnType("bigint");
b.Property<int>("ProviderID")
.HasColumnType("integer");
b.HasKey("ID");
@ -347,9 +347,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Season", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("ImgPrimary")
@ -358,17 +358,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Overview")
.HasColumnType("text");
b.Property<long>("SeasonNumber")
.HasColumnType("bigint");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<long>("ShowID")
.HasColumnType("bigint");
b.Property<int>("ShowID")
.HasColumnType("integer");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<long?>("Year")
.HasColumnType("bigint");
b.Property<int?>("Year")
.HasColumnType("integer");
b.HasKey("ID");
@ -379,9 +379,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Show", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Aliases")
@ -390,8 +390,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Backdrop")
.HasColumnType("text");
b.Property<long?>("EndYear")
.HasColumnType("bigint");
b.Property<int?>("EndYear")
.HasColumnType("integer");
b.Property<bool>("IsMovie")
.HasColumnType("boolean");
@ -411,14 +411,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Property<string>("Slug")
.HasColumnType("text");
b.Property<long?>("StartYear")
.HasColumnType("bigint");
b.Property<int?>("StartYear")
.HasColumnType("integer");
b.Property<int?>("Status")
.HasColumnType("integer");
b.Property<long?>("StudioID")
.HasColumnType("bigint");
b.Property<int?>("StudioID")
.HasColumnType("integer");
b.Property<string>("Title")
.HasColumnType("text");
@ -438,9 +438,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Studio", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Name")
@ -459,16 +459,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
modelBuilder.Entity("Kyoo.Models.Track", b =>
{
b.Property<long>("ID")
b.Property<int>("ID")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("Codec")
.HasColumnType("text");
b.Property<long>("EpisodeID")
.HasColumnType("bigint");
b.Property<int>("EpisodeID")
.HasColumnType("integer");
b.Property<bool>("IsDefault")
.HasColumnType("boolean");

View File

@ -137,6 +137,18 @@ namespace Kyoo
AllowedOrigins = { new Uri(publicUrl).GetLeftPart(UriPartial.Authority) }
});
services.AddTransient<ILibraryRepository, LibraryRepository>();
services.AddTransient<ICollectionRepository, CollectionRepository>();
services.AddTransient<IShowRepository, ShowRepository>();
services.AddTransient<ISeasonRepository, SeasonRepository>();
services.AddTransient<IEpisodeRepository, EpisodeRepository>();
services.AddTransient<ITrackRepository, TrackRepository>();
services.AddTransient<IPeopleRepository, PeopleRepository>();
services.AddTransient<IStudioRepository, StudioRepository>();
services.AddTransient<IGenreRepository, GenreRepository>();
services.AddTransient<IProviderRepository, ProviderRepository>();
services.AddScoped<ILibraryManager, LibraryManager>();
services.AddSingleton<ITranscoder, Transcoder>();
services.AddSingleton<IThumbnailsManager, ThumbnailsManager>();

View File

@ -8,7 +8,6 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Kyoo.Models.Watch;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
@ -28,11 +27,11 @@ namespace Kyoo.Controllers
private ITranscoder _transcoder;
private IConfiguration _config;
public IEnumerable<string> GetPossibleParameters()
public async Task<IEnumerable<string>> GetPossibleParameters()
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
return libraryManager.GetLibraries().Select(x => x.Slug);
return (await libraryManager.GetLibraries()).Select(x => x.Slug);
}
public int? Progress()
@ -53,28 +52,27 @@ namespace Kyoo.Controllers
{
using IServiceScope serviceScope = _serviceProvider.CreateScope();
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
IEnumerable<Episode> episodes = libraryManager.GetEpisodes();
IEnumerable<Library> libraries = argument == null
? libraryManager.GetLibraries()
: new [] {libraryManager.GetLibrary(argument)};
ICollection<Episode> episodes = await libraryManager.GetEpisodes();
ICollection<Library> libraries = argument == null
? await libraryManager.GetLibraries()
: new [] { await libraryManager.GetLibrary(argument)};
foreach (Episode episode in episodes)
{
if (!File.Exists(episode.Path))
libraryManager.RemoveEpisode(episode);
await libraryManager.DeleteEpisode(episode);
}
await libraryManager.SaveChanges();
await Task.WhenAll(libraries.ToList().Select(x => Scan(x, libraryManager, cancellationToken)));
await Task.WhenAll(libraries.Select(x => Scan(x, episodes, cancellationToken)));
}
catch (Exception ex)
{
Console.Error.WriteLine($"Unknown exception thrown durring libraries scan.\nException: {ex.Message}");
await Console.Error.WriteLineAsync($"Unknown exception thrown durring libraries scan.\nException: {ex.Message}");
}
Console.WriteLine("Scan finished!");
}
private Task Scan(Library library, ILibraryManager libraryManager, CancellationToken cancellationToken)
private Task Scan(Library library, ICollection<Episode> episodes, CancellationToken cancellationToken)
{
Console.WriteLine($"Scanning library {library.Name} at {string.Join(", ", library.Paths)}.");
return Task.WhenAll(library.Paths.Select(async path =>
@ -89,29 +87,29 @@ namespace Kyoo.Controllers
}
catch (DirectoryNotFoundException)
{
Console.Error.WriteLine($"The library's directory {path} could not be found (library slug: {library.Slug})");
await Console.Error.WriteLineAsync($"The library's directory {path} could not be found (library slug: {library.Slug})");
return Task.CompletedTask;
}
catch (PathTooLongException)
{
Console.Error.WriteLine($"The library's directory {path} is too long for this system. (library slug: {library.Slug})");
await Console.Error.WriteLineAsync($"The library's directory {path} is too long for this system. (library slug: {library.Slug})");
return Task.CompletedTask;
}
catch (ArgumentException)
{
Console.Error.WriteLine($"The library's directory {path} is invalid. (library slug: {library.Slug})");
await Console.Error.WriteLineAsync($"The library's directory {path} is invalid. (library slug: {library.Slug})");
return Task.CompletedTask;
}
catch (UnauthorizedAccessException)
{
Console.Error.WriteLine($"Permission denied: can't access library's directory at {path}. (library slug: {library.Slug})");
await Console.Error.WriteLineAsync($"Permission denied: can't access library's directory at {path}. (library slug: {library.Slug})");
return Task.CompletedTask;
}
// return Task.WhenAll(files.Select(file =>
foreach (string file in files)
{
if (!IsVideo(file) || libraryManager.GetEpisodes().Any(x => x.Path == file))
if (!IsVideo(file) || episodes.Any(x => x.Path == file))
continue; //return Task.CompletedTask;
string relativePath = file.Substring(path.Length);
/*return*/ await RegisterFile(file, relativePath, library, cancellationToken);
@ -128,7 +126,6 @@ namespace Kyoo.Controllers
using IServiceScope serviceScope = _serviceProvider.CreateScope();
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
((DbSet<Library>)libraryManager.GetLibraries()).Attach(library);
string patern = _config.GetValue<string>("regex");
Regex regex = new Regex(patern, RegexOptions.IgnoreCase);
@ -137,74 +134,97 @@ namespace Kyoo.Controllers
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;
int seasonNumber = int.TryParse(match.Groups["Season"].Value, out int tmp) ? tmp : -1;
int episodeNumber = int.TryParse(match.Groups["Episode"].Value, out tmp) ? tmp : -1;
int absoluteNumber = int.TryParse(match.Groups["Absolute"].Value, out tmp) ? tmp : -1;
Collection collection = await GetCollection(libraryManager, collectionName, library);
bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1;
Show show = await GetShow(libraryManager, showName, showPath, isMovie, library);
if (isMovie)
libraryManager.Register(await GetMovie(show, path));
await libraryManager.RegisterEpisode(await GetMovie(show, path));
else
{
Season season = await GetSeason(libraryManager, show, seasonNumber, library);
Episode episode = await GetEpisode(libraryManager, show, season, episodeNumber, absoluteNumber, path, library);
libraryManager.Register(episode);
await libraryManager.RegisterEpisode(episode);
}
if (collection != null)
libraryManager.Register(collection);
libraryManager.RegisterShowLinks(library, collection, show);
Console.WriteLine($"Registering episode at: {path}");
await libraryManager.SaveChanges();
await libraryManager.AddShowLink(show, library, collection);
Console.WriteLine($"Episode at {path} registered.");
}
private async Task<Collection> GetCollection(ILibraryManager libraryManager, string collectionName, Library library)
private async Task<Collection> GetCollection(ILibraryManager libraryManager,
string collectionName,
Library library)
{
if (string.IsNullOrEmpty(collectionName))
return await Task.FromResult<Collection>(null);
Collection name = libraryManager.GetCollection(Utility.ToSlug(collectionName));
if (name != null)
return name;
return await _metadataProvider.GetCollectionFromName(collectionName, library);
return null;
Collection collection = await libraryManager.GetCollection(Utility.ToSlug(collectionName));
if (collection != null)
return collection;
collection = await _metadataProvider.GetCollectionFromName(collectionName, library);
await libraryManager.RegisterCollection(collection);
return collection;
}
private async Task<Show> GetShow(ILibraryManager libraryManager, string showTitle, string showPath, bool isMovie, Library library)
private async Task<Show> GetShow(ILibraryManager libraryManager,
string showTitle,
string showPath,
bool isMovie,
Library library)
{
Show show = libraryManager.GetShowByPath(showPath);
Show show = await libraryManager.GetShowByPath(showPath);
if (show != null)
return show;
show = await _metadataProvider.SearchShow(showTitle, isMovie, library);
show.Path = showPath;
show.People = await _metadataProvider.GetPeople(show, library);
await libraryManager.RegisterShow(show);
await _thumbnailsManager.Validate(show.People);
await _thumbnailsManager.Validate(show);
return show;
}
private async Task<Season> GetSeason(ILibraryManager libraryManager, Show show, long seasonNumber, Library library)
private async Task<Season> GetSeason(ILibraryManager libraryManager,
Show show,
int seasonNumber,
Library library)
{
if (seasonNumber == -1)
return default;
Season season = libraryManager.GetSeason(show.Slug, seasonNumber);
Season season = await libraryManager.GetSeason(show.Slug, seasonNumber);
if (season == null)
{
season = await _metadataProvider.GetSeason(show, seasonNumber, library);
await libraryManager.RegisterSeason(season);
await _thumbnailsManager.Validate(season);
}
season.Show = show;
return season;
}
private async Task<Episode> GetEpisode(ILibraryManager libraryManager, Show show, Season season, long episodeNumber, long absoluteNumber, string episodePath, Library library)
private async Task<Episode> GetEpisode(ILibraryManager libraryManager,
Show show,
Season season,
int episodeNumber,
int absoluteNumber,
string episodePath,
Library library)
{
Episode episode = await _metadataProvider.GetEpisode(show, episodePath, season?.SeasonNumber ?? -1, episodeNumber, absoluteNumber, library);
if (season == null)
season = await GetSeason(libraryManager, show, episode.SeasonNumber, library);
Episode episode = await _metadataProvider.GetEpisode(show,
episodePath,
season?.SeasonNumber ?? -1,
episodeNumber,
absoluteNumber,
library);
season ??= await GetSeason(libraryManager, show, episode.SeasonNumber, library);
episode.Season = season;
episode.SeasonID = season?.ID;
if (season == null)
{
await Console.Error.WriteLineAsync("\tError: You don't have any provider that support absolute epiode numbering. Install one and try again.");
await Console.Error.WriteLineAsync("Error: You don't have any provider that support absolute epiode numbering. Install one and try again.");
return default;
}
@ -215,7 +235,13 @@ namespace Kyoo.Controllers
private async Task<Episode> GetMovie(Show show, string episodePath)
{
Episode episode = new Episode {Title = show.Title, Path = episodePath, Show = show};
Episode episode = new Episode
{
Title = show.Title,
Path = episodePath,
Show = show,
ShowID = show.ID
};
episode.Tracks = await GetTracks(episode);
return episode;
}

View File

@ -58,9 +58,9 @@ namespace Kyoo.Tasks
return Task.CompletedTask;
}
public IEnumerable<string> GetPossibleParameters()
public Task<IEnumerable<string>> GetPossibleParameters()
{
return null;
return Task.FromResult<IEnumerable<string>>(null);
}
public int? Progress()

View File

@ -28,9 +28,9 @@ namespace Kyoo.Tasks
return Task.CompletedTask;
}
public IEnumerable<string> GetPossibleParameters()
public Task<IEnumerable<string>> GetPossibleParameters()
{
return null;
return Task.FromResult<IEnumerable<string>>(null);
}
public int? Progress()

View File

@ -24,9 +24,9 @@ namespace Kyoo.Tasks
return Task.CompletedTask;
}
public IEnumerable<string> GetPossibleParameters()
public Task<IEnumerable<string>> GetPossibleParameters()
{
return null;
return Task.FromResult<IEnumerable<string>>(null);
}
public int? Progress()

View File

@ -62,7 +62,7 @@ namespace Kyoo.Tasks
edited.ID = old.ID;
edited.Slug = old.Slug;
edited.Path = old.Path;
await libraryManager.Edit(edited, true);
await libraryManager.EditShow(edited, true);
await _thumbnailsManager.Validate(edited, true);
}
if (old.Seasons != null)
@ -95,7 +95,7 @@ namespace Kyoo.Tasks
Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
Season edited = await _providerManager.GetSeason(show, old.SeasonNumber, library);
edited.ID = old.ID;
await libraryManager.Edit(edited, true);
await libraryManager.EditSeason(edited, true);
await _thumbnailsManager.Validate(edited, true);
}
if (old.Episodes != null)
@ -110,13 +110,13 @@ namespace Kyoo.Tasks
Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
Episode edited = await _providerManager.GetEpisode(show, old.Path, old.SeasonNumber, old.EpisodeNumber, old.AbsoluteNumber, library);
edited.ID = old.ID;
await libraryManager.Edit(edited, true);
await libraryManager.EditEpisode(edited, true);
await _thumbnailsManager.Validate(edited, true);
}
public IEnumerable<string> GetPossibleParameters()
public Task<IEnumerable<string>> GetPossibleParameters()
{
return default;
return Task.FromResult<IEnumerable<string>>(null);
}
public int? Progress()

View File

@ -2,6 +2,7 @@
using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace Kyoo.Api
@ -19,9 +20,9 @@ namespace Kyoo.Api
[HttpGet("{collectionSlug}")]
[Authorize(Policy="Read")]
public ActionResult<Collection> GetShows(string collectionSlug)
public async Task<ActionResult<Collection>> GetShows(string collectionSlug)
{
Collection collection = _libraryManager.GetCollection(collectionSlug);
Collection collection = await _libraryManager.GetCollection(collectionSlug);
if (collection == null)
return NotFound();

View File

@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Authorization;
@ -20,9 +21,9 @@ namespace Kyoo.Api
[HttpGet("{showSlug}/season/{seasonNumber}")]
[Authorize(Policy="Read")]
public ActionResult<IEnumerable<Episode>> GetEpisodesForSeason(string showSlug, long seasonNumber)
public async Task<ActionResult<IEnumerable<Episode>>> GetEpisodesForSeason(string showSlug, int seasonNumber)
{
IEnumerable<Episode> episodes = _libraryManager.GetEpisodes(showSlug, seasonNumber);
IEnumerable<Episode> episodes = await _libraryManager.GetEpisodes(showSlug, seasonNumber);
if(episodes == null)
return NotFound();
@ -33,9 +34,9 @@ namespace Kyoo.Api
[HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")]
[Authorize(Policy="Read")]
[JsonDetailed]
public ActionResult<Episode> GetEpisode(string showSlug, long seasonNumber, long episodeNumber)
public async Task<ActionResult<Episode>> GetEpisode(string showSlug, int seasonNumber, int episodeNumber)
{
Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode == null)
return NotFound();

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
@ -18,9 +19,9 @@ namespace Kyoo.API
_libraryManager = libraryManager;
}
public ActionResult<IEnumerable<Genre>> Index()
public async Task<ActionResult<IEnumerable<Genre>>> Index()
{
return _libraryManager.GetGenres().ToList();
return (await _libraryManager.GetGenres()).ToList();
}
}
}

View File

@ -3,6 +3,7 @@ using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace Kyoo.Api
@ -22,15 +23,15 @@ namespace Kyoo.Api
}
[HttpGet]
public IEnumerable<Library> GetLibraries()
public async Task<IEnumerable<Library>> GetLibraries()
{
return _libraryManager.GetLibraries();
return await _libraryManager.GetLibraries();
}
[Route("/api/library/create")]
[HttpPost]
[Authorize(Policy="Admin")]
public IActionResult CreateLibrary([FromBody] Library library)
public async Task<IActionResult> CreateLibrary([FromBody] Library library)
{
if (!ModelState.IsValid)
return BadRequest(library);
@ -40,19 +41,18 @@ namespace Kyoo.Api
return BadRequest(new {error = "The library's name must be set and not empty"});
if (library.Paths == null || !library.Paths.Any())
return BadRequest(new {error = "The library should have a least one path."});
if (_libraryManager.GetLibrary(library.Slug) != null)
if (await _libraryManager.GetLibrary(library.Slug) != null)
return BadRequest(new {error = "Duplicated library slug"});
_libraryManager.Register(library);
_libraryManager.SaveChanges();
await _libraryManager.RegisterLibrary(library);
_taskManager.StartTask("scan", library.Slug);
return Ok();
}
[HttpGet("{librarySlug}")]
[Authorize(Policy="Read")]
public ActionResult<IEnumerable<Show>> GetShows(string librarySlug)
public async Task<ActionResult<IEnumerable<Show>>> GetShows(string librarySlug)
{
Library library = _libraryManager.GetLibrary(librarySlug);
Library library = await _libraryManager.GetLibrary(librarySlug);
if (library == null)
return NotFound();

View File

@ -1,4 +1,5 @@
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Authorization;
@ -19,9 +20,9 @@ namespace Kyoo.Api
[HttpGet("{peopleSlug}")]
[Authorize(Policy="Read")]
public ActionResult<Collection> GetPeople(string peopleSlug)
public async Task<ActionResult<Collection>> GetPeople(string peopleSlug)
{
People people = _libraryManager.GetPeople(peopleSlug);
People people = await _libraryManager.GetPeople(peopleSlug);
if (people == null)
return NotFound();

View File

@ -1,4 +1,5 @@
using Kyoo.Controllers;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -18,17 +19,17 @@ namespace Kyoo.Api
[HttpGet("{query}")]
[Authorize(Policy="Read")]
public ActionResult<SearchResult> Search(string query)
public async Task<ActionResult<SearchResult>> Search(string query)
{
SearchResult result = new SearchResult
{
Query = query,
Collections = _libraryManager.SearchCollections(query),
Shows = _libraryManager.SearchShows(query),
Episodes = _libraryManager.SearchEpisodes(query),
People = _libraryManager.SearchPeople(query),
Genres = _libraryManager.SearchGenres(query),
Studios = _libraryManager.SearchStudios(query)
Collections = await _libraryManager.SearchCollections(query),
Shows = await _libraryManager.SearchShows(query),
Episodes = await _libraryManager.SearchEpisodes(query),
People = await _libraryManager.SearchPeople(query),
Genres = await _libraryManager.SearchGenres(query),
Studios = await _libraryManager.SearchStudios(query)
};
return result;
}

View File

@ -37,15 +37,18 @@ namespace Kyoo.Api
[Authorize(Policy="Read")]
public IEnumerable<Show> GetShows()
{
return _database.LibraryLinks.AsEnumerable().Select(x => x.Show ?? x.Collection.AsShow());
return _database.LibraryLinks
.Include(x => x.Show)
.Include(x => x.Collection)
.AsEnumerable().Select(x => x.Show ?? x.Collection.AsShow()).ToList();
}
[HttpGet("{slug}")]
[Authorize(Policy="Read")]
[JsonDetailed]
public ActionResult<Show> GetShow(string slug)
public async Task<ActionResult<Show>> GetShow(string slug)
{
Show show = _libraryManager.GetShow(slug);
Show show = await _libraryManager.GetShow(slug);
if (show == null)
return NotFound();
@ -55,7 +58,7 @@ namespace Kyoo.Api
[HttpPost("edit/{slug}")]
[Authorize(Policy="Write")]
public IActionResult EditShow(string slug, [FromBody] Show show)
public async Task<IActionResult> EditShow(string slug, [FromBody] Show show)
{
if (!ModelState.IsValid)
return BadRequest(show);
@ -66,7 +69,7 @@ namespace Kyoo.Api
show.ID = old.ID;
show.Slug = slug;
show.Path = old.Path;
_libraryManager.Edit(show, false);
await _libraryManager.EditShow(show, false);
return Ok();
}
@ -79,7 +82,6 @@ namespace Kyoo.Api
Show show = _database.Shows.Include(x => x.ExternalIDs).FirstOrDefault(x => x.Slug == slug);
if (show == null)
return NotFound();
show.ExternalIDs = _libraryManager.Validate(externalIDs);
_database.SaveChanges();
_taskManager.StartTask("re-scan", $"show/{slug}");
return Ok();
@ -96,7 +98,7 @@ namespace Kyoo.Api
[Authorize(Policy = "Write")]
public async Task<IActionResult> DownloadImages(string slug)
{
Show show = _libraryManager.GetShow(slug);
Show show = await _libraryManager.GetShow(slug);
if (show == null)
return NotFound();
await _thumbnailsManager.Validate(show, true);

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
@ -18,9 +19,9 @@ namespace Kyoo.API
_libraryManager = libraryManager;
}
public ActionResult<IEnumerable<Studio>> Index()
public async Task<ActionResult<IEnumerable<Studio>>> Index()
{
return _libraryManager.GetStudios().ToList();
return (await _libraryManager.GetStudios()).ToList();
}
}
}

View File

@ -14,31 +14,37 @@ namespace Kyoo.Api
public class SubtitleController : ControllerBase
{
private readonly ILibraryManager _libraryManager;
private readonly ITranscoder _transcoder;
//private readonly ITranscoder _transcoder;
public SubtitleController(ILibraryManager libraryManager, ITranscoder transcoder)
public SubtitleController(ILibraryManager libraryManager/*, ITranscoder transcoder*/)
{
_libraryManager = libraryManager;
_transcoder = transcoder;
// _transcoder = transcoder;
}
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}.{identifier}.{extension?}")]
[Authorize(Policy="Play")]
public IActionResult GetSubtitle(string showSlug, int seasonNumber, int episodeNumber, string identifier, string extension)
public async Task<IActionResult> GetSubtitle(string showSlug,
int seasonNumber,
int episodeNumber,
string identifier,
string extension)
{
string languageTag = identifier.Length == 3 ? identifier.Substring(0, 3) : null;
bool forced = identifier.Length > 4 && identifier.Substring(4) == "forced";
Track subtitle = null;
if (languageTag != null)
subtitle = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Tracks
subtitle = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Tracks
.FirstOrDefault(x => x.Language == languageTag && x.IsForced == forced);
if (subtitle == null)
{
string idString = identifier.IndexOf('-') != -1 ? identifier.Substring(0, identifier.IndexOf('-')) : identifier;
long.TryParse(idString, out long id);
subtitle = _libraryManager.GetTracks().FirstOrDefault(x => x.ID == id);
string idString = identifier.IndexOf('-') != -1
? identifier.Substring(0, identifier.IndexOf('-'))
: identifier;
int.TryParse(idString, out int id);
subtitle = await _libraryManager.GetTrack(id);
}
if (subtitle == null)
@ -57,43 +63,43 @@ namespace Kyoo.Api
return PhysicalFile(subtitle.Path, mime);
}
[HttpGet("extract/{showSlug}-s{seasonNumber}e{episodeNumber}")]
[Authorize(Policy="Admin")]
public async Task<string> ExtractSubtitle(string showSlug, long seasonNumber, long episodeNumber)
{
Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
episode.Tracks = null;
Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path);
foreach (Track track in tracks)
{
track.EpisodeID = episode.ID;
_libraryManager.Register(track);
}
await _libraryManager.SaveChanges();
return "Done. " + tracks.Length + " track(s) extracted.";
}
[HttpGet("extract/{showSlug}")]
[Authorize(Policy="Admin")]
public async Task<string> ExtractSubtitle(string showSlug)
{
IEnumerable<Episode> episodes = _libraryManager.GetShow(showSlug).Episodes;
foreach (Episode episode in episodes)
{
episode.Tracks = null;
Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path);
foreach (Track track in tracks)
{
track.EpisodeID = episode.ID;
_libraryManager.Register(track);
}
await _libraryManager.SaveChanges();
}
return "Done.";
}
// [HttpGet("extract/{showSlug}-s{seasonNumber}e{episodeNumber}")]
// [Authorize(Policy="Admin")]
// public async Task<string> ExtractSubtitle(string showSlug, long seasonNumber, long episodeNumber)
// {
// Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
// episode.Tracks = null;
//
// Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path);
// foreach (Track track in tracks)
// {
// track.EpisodeID = episode.ID;
// _libraryManager.Register(track);
// }
// await _libraryManager.SaveChanges();
// return "Done. " + tracks.Length + " track(s) extracted.";
// }
//
// [HttpGet("extract/{showSlug}")]
// [Authorize(Policy="Admin")]
// public async Task<string> ExtractSubtitle(string showSlug)
// {
// IEnumerable<Episode> episodes = _libraryManager.GetShow(showSlug).Episodes;
// foreach (Episode episode in episodes)
// {
// episode.Tracks = null;
//
// Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path);
// foreach (Track track in tracks)
// {
// track.EpisodeID = episode.ID;
// _libraryManager.Register(track);
// }
// await _libraryManager.SaveChanges();
// }
//
// return "Done.";
// }
}

View File

@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Authorization;
@ -20,9 +21,9 @@ namespace Kyoo.Api
[HttpGet("poster/{showSlug}")]
[Authorize(Policy="Read")]
public IActionResult GetShowThumb(string showSlug)
public async Task<IActionResult> GetShowThumb(string showSlug)
{
string path = _libraryManager.GetShow(showSlug)?.Path;
string path = (await _libraryManager.GetShow(showSlug))?.Path;
if (path == null)
return NotFound();
@ -35,9 +36,9 @@ namespace Kyoo.Api
[HttpGet("logo/{showSlug}")]
[Authorize(Policy="Read")]
public IActionResult GetShowLogo(string showSlug)
public async Task<IActionResult> GetShowLogo(string showSlug)
{
string path = _libraryManager.GetShow(showSlug)?.Path;
string path = (await _libraryManager.GetShow(showSlug))?.Path;
if (path == null)
return NotFound();
@ -50,9 +51,9 @@ namespace Kyoo.Api
[HttpGet("backdrop/{showSlug}")]
[Authorize(Policy="Read")]
public IActionResult GetShowBackdrop(string showSlug)
public async Task<IActionResult> GetShowBackdrop(string showSlug)
{
string path = _libraryManager.GetShow(showSlug)?.Path;
string path = (await _libraryManager.GetShow(showSlug))?.Path;
if (path == null)
return NotFound();
@ -76,9 +77,9 @@ namespace Kyoo.Api
[HttpGet("thumb/{showSlug}-s{seasonNumber}e{episodeNumber}")]
[Authorize(Policy="Read")]
public IActionResult GetEpisodeThumb(string showSlug, long seasonNumber, long episodeNumber)
public async Task<IActionResult> GetEpisodeThumb(string showSlug, int seasonNumber, int episodeNumber)
{
string path = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Path;
string path = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Path;
if (path == null)
return NotFound();

View File

@ -27,9 +27,9 @@ namespace Kyoo.Api
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
[Authorize(Policy="Play")]
public IActionResult Index(string showSlug, long seasonNumber, long episodeNumber)
public async Task<IActionResult> Index(string showSlug, int seasonNumber, int episodeNumber)
{
Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode != null && System.IO.File.Exists(episode.Path))
return PhysicalFile(episode.Path, "video/x-matroska", true);
@ -38,9 +38,9 @@ namespace Kyoo.Api
[HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}")]
[Authorize(Policy="Play")]
public async Task<IActionResult> Transmux(string showSlug, long seasonNumber, long episodeNumber)
public async Task<IActionResult> Transmux(string showSlug, int seasonNumber, int episodeNumber)
{
Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode == null || !System.IO.File.Exists(episode.Path))
return NotFound();
@ -61,9 +61,9 @@ namespace Kyoo.Api
[HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")]
[Authorize(Policy="Play")]
public async Task<IActionResult> Transcode(string showSlug, long seasonNumber, long episodeNumber)
public async Task<IActionResult> Transcode(string showSlug, int seasonNumber, int episodeNumber)
{
Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode == null || !System.IO.File.Exists(episode.Path))
return NotFound();
@ -85,9 +85,9 @@ namespace Kyoo.Api
[HttpGet("{movieSlug}")]
[Authorize(Policy="Play")]
public IActionResult Index(string movieSlug)
public async Task<IActionResult> Index(string movieSlug)
{
Episode episode = _libraryManager.GetMovieEpisode(movieSlug);
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
if (episode != null && System.IO.File.Exists(episode.Path))
return PhysicalFile(episode.Path, "video/webm", true);
@ -98,7 +98,7 @@ namespace Kyoo.Api
[Authorize(Policy="Play")]
public async Task<IActionResult> Transmux(string movieSlug)
{
Episode episode = _libraryManager.GetMovieEpisode(movieSlug);
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
if (episode == null || !System.IO.File.Exists(episode.Path))
return NotFound();
@ -112,7 +112,7 @@ namespace Kyoo.Api
[Authorize(Policy="Play")]
public async Task<IActionResult> Transcode(string movieSlug)
{
Episode episode = _libraryManager.GetMovieEpisode(movieSlug);
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
if (episode == null || !System.IO.File.Exists(episode.Path))
return NotFound();

View File

@ -1,4 +1,5 @@
using Kyoo.Controllers;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -18,9 +19,9 @@ namespace Kyoo.Api
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
[Authorize(Policy="Read")]
public ActionResult<WatchItem> Index(string showSlug, long seasonNumber, long episodeNumber)
public async Task<ActionResult<WatchItem>> Index(string showSlug, int seasonNumber, int episodeNumber)
{
Episode item = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
Episode item = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if(item == null)
return NotFound();
@ -30,9 +31,9 @@ namespace Kyoo.Api
[HttpGet("{movieSlug}")]
[Authorize(Policy="Read")]
public ActionResult<WatchItem> Index(string movieSlug)
public async Task<ActionResult<WatchItem>> Index(string movieSlug)
{
Episode item = _libraryManager.GetMovieEpisode(movieSlug);
Episode item = await _libraryManager.GetMovieEpisode(movieSlug);
if(item == null)
return NotFound();