Reworking the LibraryManager's interface

This commit is contained in:
Zoe Roux 2021-04-18 02:17:15 +02:00
parent 5f1b45b207
commit 7dbb84780c
33 changed files with 1008 additions and 1067 deletions

View File

@ -5,284 +5,454 @@ using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Kyoo.Models; using Kyoo.Models;
using Kyoo.Models.Exceptions;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
/// <summary>
/// An interface to interract with the database. Every repository is mapped through here.
/// </summary>
public interface ILibraryManager : IDisposable, IAsyncDisposable public interface ILibraryManager : IDisposable, IAsyncDisposable
{ {
// Repositories /// <summary>
/// Get the repository corresponding to the T item.
/// </summary>
/// <typeparam name="T">The type you want</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <returns>The repository corresponding</returns>
IRepository<T> GetRepository<T>() where T : class, IResource;
/// <summary>
/// The repository that handle libraries.
/// </summary>
ILibraryRepository LibraryRepository { get; } ILibraryRepository LibraryRepository { get; }
/// <summary>
/// The repository that handle libraries's items (a wrapper arround shows & collections).
/// </summary>
ILibraryItemRepository LibraryItemRepository { get; } ILibraryItemRepository LibraryItemRepository { get; }
/// <summary>
/// The repository that handle collections.
/// </summary>
ICollectionRepository CollectionRepository { get; } ICollectionRepository CollectionRepository { get; }
/// <summary>
/// The repository that handle shows.
/// </summary>
IShowRepository ShowRepository { get; } IShowRepository ShowRepository { get; }
/// <summary>
/// The repository that handle seasons.
/// </summary>
ISeasonRepository SeasonRepository { get; } ISeasonRepository SeasonRepository { get; }
/// <summary>
/// The repository that handle episodes.
/// </summary>
IEpisodeRepository EpisodeRepository { get; } IEpisodeRepository EpisodeRepository { get; }
/// <summary>
/// The repository that handle tracks.
/// </summary>
ITrackRepository TrackRepository { get; } ITrackRepository TrackRepository { get; }
/// <summary>
/// The repository that handle people.
/// </summary>
IPeopleRepository PeopleRepository { get; } IPeopleRepository PeopleRepository { get; }
/// <summary>
/// The repository that handle studios.
/// </summary>
IStudioRepository StudioRepository { get; } IStudioRepository StudioRepository { get; }
/// <summary>
/// The repository that handle genres.
/// </summary>
IGenreRepository GenreRepository { get; } IGenreRepository GenreRepository { get; }
/// <summary>
/// The repository that handle providers.
/// </summary>
IProviderRepository ProviderRepository { get; } IProviderRepository ProviderRepository { get; }
// Get by id /// <summary>
Task<Library> GetLibrary(int id); /// Get the resource by it's ID
Task<Collection> GetCollection(int id); /// </summary>
Task<Show> GetShow(int id); /// <param name="id">The id of the resource</param>
Task<Season> GetSeason(int id); /// <typeparam name="T">The type of the resource</typeparam>
Task<Season> GetSeason(int showID, int seasonNumber); /// <exception cref="ItemNotFound">If the item is not found</exception>
Task<Episode> GetEpisode(int id); /// <returns>The resource found</returns>
Task<Episode> GetEpisode(int showID, int seasonNumber, int episodeNumber); Task<T> Get<T>(int id) where T : class, IResource;
Task<Genre> GetGenre(int id);
Task<Track> GetTrack(int id);
Task<Studio> GetStudio(int id);
Task<People> GetPeople(int id);
Task<ProviderID> GetProvider(int id);
// Get by slug /// <summary>
Task<Library> GetLibrary(string slug); /// Get the resource by it's slug
Task<Collection> GetCollection(string slug); /// </summary>
Task<Show> GetShow(string slug); /// <param name="slug">The slug of the resource</param>
Task<Season> GetSeason(string slug); /// <typeparam name="T">The type of the resource</typeparam>
Task<Season> GetSeason(string showSlug, int seasonNumber); /// <exception cref="ItemNotFound">If the item is not found</exception>
Task<Episode> GetEpisode(string slug); /// <returns>The resource found</returns>
Task<Episode> GetEpisode(string showSlug, int seasonNumber, int episodeNumber); Task<T> Get<T>(string slug) where T : class, IResource;
Task<Episode> GetMovieEpisode(string movieSlug);
/// <summary>
/// Get the resource by a filter function.
/// </summary>
/// <param name="where">The filter function.</param>
/// <typeparam name="T">The type of the resource</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <returns>The first resource found that match the where function</returns>
Task<T> Get<T>(Expression<Func<T, bool>> where) where T : class, IResource;
/// <summary>
/// Get a season from it's showID and it's seasonNumber
/// </summary>
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(int showID, int seasonNumber);
/// <summary>
/// Get a season from it's show slug and it's seasonNumber
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(string showSlug, int seasonNumber);
/// <summary>
/// Get a episode from it's showID, it's seasonNumber and it's episode number.
/// </summary>
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <param name="episodeNumber">The episode's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> Get(int showID, int seasonNumber, int episodeNumber);
/// <summary>
/// Get a episode from it's show slug, it's seasonNumber and it's episode number.
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <param name="episodeNumber">The episode's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber);
/// <summary>
/// Get a tracck from it's slug and it's type.
/// </summary>
/// <param name="slug">The slug of the track</param>
/// <param name="type">The type (Video, Audio or Subtitle)</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <returns>The tracl found</returns>
Task<Track> GetTrack(string slug, StreamType type = StreamType.Unknown); Task<Track> GetTrack(string slug, StreamType type = StreamType.Unknown);
Task<Genre> GetGenre(string slug);
Task<Studio> GetStudio(string slug);
Task<People> GetPeople(string slug);
Task<ProviderID> GetProvider(string slug);
// Get by predicate
Task<Library> GetLibrary(Expression<Func<Library, bool>> where);
Task<Collection> GetCollection(Expression<Func<Collection, bool>> where);
Task<Show> GetShow(Expression<Func<Show, bool>> where);
Task<Season> GetSeason(Expression<Func<Season, bool>> where);
Task<Episode> GetEpisode(Expression<Func<Episode, bool>> where);
Task<Track> GetTrack(Expression<Func<Track, bool>> where);
Task<Genre> GetGenre(Expression<Func<Genre, bool>> where);
Task<Studio> GetStudio(Expression<Func<Studio, bool>> where);
Task<People> GetPerson(Expression<Func<People, bool>> where);
/// <summary>
/// Load a related resource
/// </summary>
/// <param name="obj">The source object.</param>
/// <param name="member">A getter function for the member to load</param>
/// <typeparam name="T">The type of the source object</typeparam>
/// <typeparam name="T2">The related resource's type</typeparam>
/// <returns>The param <see cref="obj"/></returns>
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, T2>> member) Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, T2>> member)
where T : class, IResource where T : class, IResource
where T2 : class, IResource, new(); where T2 : class, IResource, new();
/// <summary>
/// Load a collection of related resource
/// </summary>
/// <param name="obj">The source object.</param>
/// <param name="member">A getter function for the member to load</param>
/// <typeparam name="T">The type of the source object</typeparam>
/// <typeparam name="T2">The related resource's type</typeparam>
/// <returns>The param <see cref="obj"/></returns>
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, ICollection<T2>>> member) Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, ICollection<T2>>> member)
where T : class, IResource where T : class, IResource
where T2 : class, new(); where T2 : class, new();
/// <summary>
/// Load a related resource by it's name
/// </summary>
/// <param name="obj">The source object.</param>
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
/// <typeparam name="T">The type of the source object</typeparam>
/// <returns>The param <see cref="obj"/></returns>
Task<T> Load<T>([NotNull] T obj, string memberName) Task<T> Load<T>([NotNull] T obj, string memberName)
where T : class, IResource; where T : class, IResource;
/// <summary>
/// Load a related resource without specifing it's type.
/// </summary>
/// <param name="obj">The source object.</param>
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
Task Load([NotNull] IResource obj, string memberName); Task Load([NotNull] IResource obj, string memberName);
// Library Items relations /// <summary>
/// Get items (A wrapper arround shows or collections) from a library.
/// </summary>
/// <param name="id">The ID of the library</param>
/// <param name="where">A filter function</param>
/// <param name="sort">Sort informations (sort order & sort by)</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id, Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id,
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary>
/// Get items (A wrapper arround shows or collections) from a library.
/// </summary>
/// <param name="id">The ID of the library</param>
/// <param name="where">A filter function</param>
/// <param name="sort">A sort by method</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id, Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id,
[Optional] Expression<Func<LibraryItem, bool>> where, [Optional] Expression<Func<LibraryItem, bool>> where,
Expression<Func<LibraryItem, object>> sort, Expression<Func<LibraryItem, object>> sort,
Pagination limit = default Pagination limit = default
) => GetItemsFromLibrary(id, where, new Sort<LibraryItem>(sort), limit); ) => GetItemsFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
Task<ICollection<LibraryItem>> GetItemsFromLibrary(string librarySlug, /// <summary>
/// Get items (A wrapper arround shows or collections) from a library.
/// </summary>
/// <param name="slug">The slug of the library</param>
/// <param name="where">A filter function</param>
/// <param name="sort">Sort informations (sort order & sort by)</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<LibraryItem>> GetItemsFromLibrary(string slug,
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default); Pagination limit = default);
Task<ICollection<LibraryItem>> GetItemsFromLibrary(string librarySlug,
/// <summary>
/// Get items (A wrapper arround shows or collections) from a library.
/// </summary>
/// <param name="slug">The slug of the library</param>
/// <param name="where">A filter function</param>
/// <param name="sort">A sort by method</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<LibraryItem>> GetItemsFromLibrary(string slug,
[Optional] Expression<Func<LibraryItem, bool>> where, [Optional] Expression<Func<LibraryItem, bool>> where,
Expression<Func<LibraryItem, object>> sort, Expression<Func<LibraryItem, object>> sort,
Pagination limit = default Pagination limit = default
) => GetItemsFromLibrary(librarySlug, where, new Sort<LibraryItem>(sort), limit); ) => GetItemsFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit);
// People Role relations
/// <summary>
/// Get people's roles from a show.
/// </summary>
/// <param name="showID">The ID of the show</param>
/// <param name="where">A filter function</param>
/// <param name="sort">Sort informations (sort order & sort by)</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID, Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary>
/// Get people's roles from a show.
/// </summary>
/// <param name="showID">The ID of the show</param>
/// <param name="where">A filter function</param>
/// <param name="sort">A sort by method</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID, Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
[Optional] Expression<Func<PeopleRole, bool>> where, [Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetPeopleFromShow(showID, where, new Sort<PeopleRole>(sort), limit); ) => GetPeopleFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
/// <summary>
/// Get people's roles from a show.
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="where">A filter function</param>
/// <param name="sort">Sort informations (sort order & sort by)</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug, Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary>
/// Get people's roles from a show.
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="where">A filter function</param>
/// <param name="sort">A sort by method</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug, Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
[Optional] Expression<Func<PeopleRole, bool>> where, [Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit); ) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
// Show Role relations
Task<ICollection<PeopleRole>> GetRolesFromPeople(int showID, /// <summary>
/// Get people's roles from a person.
/// </summary>
/// <param name="id">The id of the person</param>
/// <param name="where">A filter function</param>
/// <param name="sort">Sort informations (sort order & sort by)</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
Task<ICollection<PeopleRole>> GetRolesFromPeople(int showID,
/// <summary>
/// Get people's roles from a person.
/// </summary>
/// <param name="id">The id of the person</param>
/// <param name="where">A filter function</param>
/// <param name="sort">A sort by method</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
[Optional] Expression<Func<PeopleRole, bool>> where, [Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetRolesFromPeople(showID, where, new Sort<PeopleRole>(sort), limit); ) => GetRolesFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
Task<ICollection<PeopleRole>> GetRolesFromPeople(string showSlug, /// <summary>
/// Get people's roles from a person.
/// </summary>
/// <param name="slug">The slug of the person</param>
/// <param name="where">A filter function</param>
/// <param name="sort">Sort informations (sort order & sort by)</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
Task<ICollection<PeopleRole>> GetRolesFromPeople(string showSlug,
/// <summary>
/// Get people's roles from a person.
/// </summary>
/// <param name="slug">The slug of the person</param>
/// <param name="where">A filter function</param>
/// <param name="sort">A sort by method</param>
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
[Optional] Expression<Func<PeopleRole, bool>> where, [Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetRolesFromPeople(showSlug, where, new Sort<PeopleRole>(sort), limit); ) => GetRolesFromPeople(slug, where, new Sort<PeopleRole>(sort), limit);
// Helpers
/// <summary>
/// Setup relations between a show, a library and a collection
/// </summary>
/// <param name="showID">The show's ID to setup relations with</param>
/// <param name="libraryID">The library's ID to setup relations with (optional)</param>
/// <param name="collectionID">The collection's ID to setup relations with (optional)</param>
Task AddShowLink(int showID, int? libraryID, int? collectionID); Task AddShowLink(int showID, int? libraryID, int? collectionID);
/// <summary>
/// Setup relations between a show, a library and a collection
/// </summary>
/// <param name="show">The show to setup relations with</param>
/// <param name="library">The library to setup relations with (optional)</param>
/// <param name="collection">The collection to setup relations with (optional)</param>
Task AddShowLink([NotNull] Show show, Library library, Collection collection); Task AddShowLink([NotNull] Show show, Library library, Collection collection);
// Get all /// <summary>
Task<ICollection<Library>> GetLibraries(Expression<Func<Library, bool>> where = null, /// Get all resources with filters
Sort<Library> sort = default, /// </summary>
Pagination limit = default); /// <param name="where">A filter function</param>
Task<ICollection<Collection>> GetCollections(Expression<Func<Collection, bool>> where = null, /// <param name="sort">Sort informations (sort order & sort by)</param>
Sort<Collection> sort = default, /// <param name="limit">How many items to return and where to start</param>
Pagination limit = default); /// <typeparam name="T">The type of resources to load</typeparam>
Task<ICollection<Show>> GetShows(Expression<Func<Show, bool>> where = null, /// <returns>A list of resources that match every filters</returns>
Sort<Show> sort = default, Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null,
Pagination limit = default); Sort<T> sort = default,
Task<ICollection<Season>> GetSeasons(Expression<Func<Season, bool>> where = null, Pagination limit = default) where T : class, IResource;
Sort<Season> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodes(Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Track>> GetTracks(Expression<Func<Track, bool>> where = null,
Sort<Track> sort = default,
Pagination limit = default);
Task<ICollection<Studio>> GetStudios(Expression<Func<Studio, bool>> where = null,
Sort<Studio> sort = default,
Pagination limit = default);
Task<ICollection<People>> GetPeople(Expression<Func<People, bool>> where = null,
Sort<People> sort = default,
Pagination limit = default);
Task<ICollection<Genre>> GetGenres(Expression<Func<Genre, bool>> where = null,
Sort<Genre> sort = default,
Pagination limit = default);
Task<ICollection<ProviderID>> GetProviders(Expression<Func<ProviderID, bool>> where = null,
Sort<ProviderID> sort = default,
Pagination limit = default);
Task<ICollection<Library>> GetLibraries([Optional] Expression<Func<Library, bool>> where, /// <summary>
Expression<Func<Library, object>> sort, /// Get all resources with filters
Pagination limit = default /// </summary>
) => GetLibraries(where, new Sort<Library>(sort), limit); /// <param name="where">A filter function</param>
Task<ICollection<Collection>> GetCollections([Optional] Expression<Func<Collection, bool>> where, /// <param name="sort">A sort by function</param>
Expression<Func<Collection, object>> sort, /// <param name="limit">How many items to return and where to start</param>
Pagination limit = default /// <typeparam name="T">The type of resources to load</typeparam>
) => GetCollections(where, new Sort<Collection>(sort), limit); /// <returns>A list of resources that match every filters</returns>
Task<ICollection<Show>> GetShows([Optional] Expression<Func<Show, bool>> where, Task<ICollection<T>> GetAll<T>([Optional] Expression<Func<T, bool>> where,
Expression<Func<Show, object>> sort, Expression<Func<T, object>> sort,
Pagination limit = default Pagination limit = default) where T : class, IResource
) => GetShows(where, new Sort<Show>(sort), limit); {
Task<ICollection<Track>> GetTracks([Optional] Expression<Func<Track, bool>> where, return GetAll(where, new Sort<T>(sort), limit);
Expression<Func<Track, object>> sort, }
Pagination limit = default
) => GetTracks(where, new Sort<Track>(sort), limit);
Task<ICollection<Studio>> GetStudios([Optional] Expression<Func<Studio, bool>> where,
Expression<Func<Studio, object>> sort,
Pagination limit = default
) => GetStudios(where, new Sort<Studio>(sort), limit);
Task<ICollection<People>> GetPeople([Optional] Expression<Func<People, bool>> where,
Expression<Func<People, object>> sort,
Pagination limit = default
) => GetPeople(where, new Sort<People>(sort), limit);
Task<ICollection<Genre>> GetGenres([Optional] Expression<Func<Genre, bool>> where,
Expression<Func<Genre, object>> sort,
Pagination limit = default
) => GetGenres(where, new Sort<Genre>(sort), limit);
Task<ICollection<ProviderID>> GetProviders([Optional] Expression<Func<ProviderID, bool>> where,
Expression<Func<ProviderID, object>> sort,
Pagination limit = default
) => GetProviders(where, new Sort<ProviderID>(sort), limit);
/// <summary>
/// Get the count of resources that match the filter
/// </summary>
/// <param name="where">A filter function</param>
/// <typeparam name="T">The type of resources to load</typeparam>
/// <returns>A list of resources that match every filters</returns>
Task<int> GetCount<T>(Expression<Func<T, bool>> where = null) where T : class, IResource;
// Counts /// <summary>
Task<int> GetLibrariesCount(Expression<Func<Library, bool>> where = null); /// Search for a resource
Task<int> GetCollectionsCount(Expression<Func<Collection, bool>> where = null); /// </summary>
Task<int> GetShowsCount(Expression<Func<Show, bool>> where = null); /// <param name="query">The search query</param>
Task<int> GetSeasonsCount(Expression<Func<Season, bool>> where = null); /// <typeparam name="T">The type of resources</typeparam>
Task<int> GetEpisodesCount(Expression<Func<Episode, bool>> where = null); /// <returns>A list of 20 items that match the search query</returns>
Task<int> GetTracksCount(Expression<Func<Track, bool>> where = null); Task<ICollection<T>> Search<T>(string query) where T : class, IResource;
Task<int> GetGenresCount(Expression<Func<Genre, bool>> where = null);
Task<int> GetStudiosCount(Expression<Func<Studio, bool>> where = null);
Task<int> GetPeopleCount(Expression<Func<People, bool>> where = null);
// Search /// <summary>
Task<ICollection<Library>> SearchLibraries(string searchQuery); /// Create a new resource.
Task<ICollection<Collection>> SearchCollections(string searchQuery); /// </summary>
Task<ICollection<Show>> SearchShows(string searchQuery); /// <param name="item">The item to register</param>
Task<ICollection<Season>> SearchSeasons(string searchQuery); /// <typeparam name="T">The type of resource</typeparam>
Task<ICollection<Episode>> SearchEpisodes(string searchQuery); /// <returns>The resource registers and completed by database's informations (related items & so on)</returns>
Task<ICollection<Genre>> SearchGenres(string searchQuery); Task<T> Create<T>(T item) where T : class, IResource;
Task<ICollection<Studio>> SearchStudios(string searchQuery);
Task<ICollection<People>> SearchPeople(string searchQuery);
//Register values /// <summary>
Task<Library> RegisterLibrary(Library library); /// Edit a resource
Task<Collection> RegisterCollection(Collection collection); /// </summary>
Task<Show> RegisterShow(Show show); /// <param name="item">The resourcce to edit, it's ID can't change.</param>
Task<Season> RegisterSeason(Season season); /// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
Task<Episode> RegisterEpisode(Episode episode); /// <typeparam name="T">The type of resources</typeparam>
Task<Track> RegisterTrack(Track track); /// <returns>The resource edited and completed by database's informations (related items & so on)</returns>
Task<Genre> RegisterGenre(Genre genre); Task<T> Edit<T>(T item, bool resetOld) where T : class, IResource;
Task<Studio> RegisterStudio(Studio studio);
Task<People> RegisterPeople(People people);
// Edit values /// <summary>
Task<Library> EditLibrary(Library library, bool resetOld); /// Delete a resource.
Task<Collection> EditCollection(Collection collection, bool resetOld); /// </summary>
Task<Show> EditShow(Show show, bool resetOld); /// <param name="item">The resource to delete</param>
Task<Season> EditSeason(Season season, bool resetOld); /// <typeparam name="T">The type of resource to delete</typeparam>
Task<Episode> EditEpisode(Episode episode, bool resetOld); Task Delete<T>(T item) where T : class, IResource;
Task<Track> EditTrack(Track track, bool resetOld);
Task<Genre> EditGenre(Genre genre, bool resetOld);
Task<Studio> EditStudio(Studio studio, bool resetOld);
Task<People> EditPeople(People people, bool resetOld);
// Delete values /// <summary>
Task DeleteLibrary(Library library); /// Delete a resource by it's ID.
Task DeleteCollection(Collection collection); /// </summary>
Task DeleteShow(Show show); /// <param name="id">The id of the resource to delete</param>
Task DeleteSeason(Season season); /// <typeparam name="T">The type of resource to delete</typeparam>
Task DeleteEpisode(Episode episode); Task Delete<T>(int id) where T : class, IResource;
Task DeleteTrack(Track track);
Task DeleteGenre(Genre genre);
Task DeleteStudio(Studio studio);
Task DeletePeople(People people);
//Delete by slug /// <summary>
Task DeleteLibrary(string slug); /// Delete a resource by it's slug.
Task DeleteCollection(string slug); /// </summary>
Task DeleteShow(string slug); /// <param name="slug">The slug of the resource to delete</param>
Task DeleteSeason(string slug); /// <typeparam name="T">The type of resource to delete</typeparam>
Task DeleteEpisode(string slug); Task Delete<T>(string slug) where T : class, IResource;
Task DeleteTrack(string slug);
Task DeleteGenre(string slug);
Task DeleteStudio(string slug);
Task DeletePeople(string slug);
//Delete by id
Task DeleteLibrary(int id);
Task DeleteCollection(int id);
Task DeleteShow(int id);
Task DeleteSeason(int id);
Task DeleteEpisode(int id);
Task DeleteTrack(int id);
Task DeleteGenre(int id);
Task DeleteStudio(int id);
Task DeletePeople(int id);
} }
} }

View File

@ -6,7 +6,7 @@ namespace Kyoo.Controllers
{ {
public interface IMetadataProvider public interface IMetadataProvider
{ {
ProviderID Provider { get; } Provider Provider { get; }
Task<Collection> GetCollectionFromName(string name); Task<Collection> GetCollectionFromName(string name);

View File

@ -23,10 +23,10 @@ namespace Kyoo.Controllers
public static implicit operator Pagination(int limit) => new(limit); public static implicit operator Pagination(int limit) => new(limit);
} }
public struct Sort<T> public readonly struct Sort<T>
{ {
public Expression<Func<T, object>> Key; public Expression<Func<T, object>> Key { get; }
public bool Descendant; public bool Descendant { get; }
public Sort(Expression<Func<T, object>> key, bool descendant = false) public Sort(Expression<Func<T, object>> key, bool descendant = false)
{ {
@ -46,8 +46,8 @@ namespace Kyoo.Controllers
return; return;
} }
string key = sortBy.Contains(':') ? sortBy.Substring(0, sortBy.IndexOf(':')) : sortBy; string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy;
string order = sortBy.Contains(':') ? sortBy.Substring(sortBy.IndexOf(':') + 1) : null; string order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null;
ParameterExpression param = Expression.Parameter(typeof(T), "x"); ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression property = Expression.Property(param, key); MemberExpression property = Expression.Property(param, key);
@ -65,7 +65,12 @@ namespace Kyoo.Controllers
} }
} }
public interface IRepository<T> : IDisposable, IAsyncDisposable where T : class, IResource public interface IBaseRepository : IDisposable, IAsyncDisposable
{
Type RepositoryType { get; }
}
public interface IRepository<T> : IBaseRepository where T : class, IResource
{ {
Task<T> Get(int id); Task<T> Get(int id);
Task<T> Get(string slug); Task<T> Get(string slug);
@ -204,7 +209,7 @@ namespace Kyoo.Controllers
) => GetFromPeople(showSlug, where, new Sort<PeopleRole>(sort), limit); ) => GetFromPeople(showSlug, where, new Sort<PeopleRole>(sort), limit);
} }
public interface IProviderRepository : IRepository<ProviderID> public interface IProviderRepository : IRepository<Provider>
{ {
Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null, Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null,
Sort<MetadataID> sort = default, Sort<MetadataID> sort = default,

View File

@ -11,7 +11,7 @@ namespace Kyoo.Controllers
Task Validate(Season season, bool alwaysDownload = false); Task Validate(Season season, bool alwaysDownload = false);
Task Validate(Episode episode, bool alwaysDownload = false); Task Validate(Episode episode, bool alwaysDownload = false);
Task Validate(People actors, bool alwaysDownload = false); Task Validate(People actors, bool alwaysDownload = false);
Task Validate(ProviderID actors, bool alwaysDownload = false); Task Validate(Provider actors, bool alwaysDownload = false);
Task<string> GetShowPoster([NotNull] Show show); Task<string> GetShowPoster([NotNull] Show show);
Task<string> GetShowLogo([NotNull] Show show); Task<string> GetShowLogo([NotNull] Show show);
@ -19,6 +19,6 @@ namespace Kyoo.Controllers
Task<string> GetSeasonPoster([NotNull] Season season); Task<string> GetSeasonPoster([NotNull] Season season);
Task<string> GetEpisodeThumb([NotNull] Episode episode); Task<string> GetEpisodeThumb([NotNull] Episode episode);
Task<string> GetPeoplePoster([NotNull] People people); Task<string> GetPeoplePoster([NotNull] People people);
Task<string> GetProviderLogo([NotNull] ProviderID provider); Task<string> GetProviderLogo([NotNull] Provider provider);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ namespace Kyoo.Models
{ {
[SerializeIgnore] public int ID { get; set; } [SerializeIgnore] public int ID { get; set; }
[SerializeIgnore] public int ProviderID { get; set; } [SerializeIgnore] public int ProviderID { get; set; }
public virtual ProviderID Provider {get; set; } public virtual Provider Provider {get; set; }
[SerializeIgnore] public int? ShowID { get; set; } [SerializeIgnore] public int? ShowID { get; set; }
[SerializeIgnore] public virtual Show Show { get; set; } [SerializeIgnore] public virtual Show Show { get; set; }
@ -25,7 +25,7 @@ namespace Kyoo.Models
public MetadataID() { } public MetadataID() { }
public MetadataID(ProviderID provider, string dataID, string link) public MetadataID(Provider provider, string dataID, string link)
{ {
Provider = provider; Provider = provider;
DataID = dataID; DataID = dataID;

View File

@ -11,20 +11,20 @@ namespace Kyoo.Models
public string Name { get; set; } public string Name { get; set; }
public string[] Paths { get; set; } public string[] Paths { get; set; }
[EditableRelation] [LoadableRelation] public virtual ICollection<ProviderID> Providers { get; set; } [EditableRelation] [LoadableRelation] public virtual ICollection<Provider> Providers { get; set; }
[LoadableRelation] public virtual ICollection<Show> Shows { get; set; } [LoadableRelation] public virtual ICollection<Show> Shows { get; set; }
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; } [LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
#if ENABLE_INTERNAL_LINKS #if ENABLE_INTERNAL_LINKS
[SerializeIgnore] public virtual ICollection<Link<Library, ProviderID>> ProviderLinks { get; set; } [SerializeIgnore] public virtual ICollection<Link<Library, Provider>> ProviderLinks { get; set; }
[SerializeIgnore] public virtual ICollection<Link<Library, Show>> ShowLinks { get; set; } [SerializeIgnore] public virtual ICollection<Link<Library, Show>> ShowLinks { get; set; }
[SerializeIgnore] public virtual ICollection<Link<Library, Collection>> CollectionLinks { get; set; } [SerializeIgnore] public virtual ICollection<Link<Library, Collection>> CollectionLinks { get; set; }
#endif #endif
public Library() { } public Library() { }
public Library(string slug, string name, IEnumerable<string> paths, IEnumerable<ProviderID> providers) public Library(string slug, string name, IEnumerable<string> paths, IEnumerable<Provider> providers)
{ {
Slug = slug; Slug = slug;
Name = name; Name = name;

View File

@ -3,7 +3,7 @@ using Kyoo.Models.Attributes;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class ProviderID : IResource public class Provider : IResource
{ {
public int ID { get; set; } public int ID { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
@ -13,20 +13,20 @@ namespace Kyoo.Models
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; } [LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
#if ENABLE_INTERNAL_LINKS #if ENABLE_INTERNAL_LINKS
[SerializeIgnore] public virtual ICollection<Link<Library, ProviderID>> LibraryLinks { get; set; } [SerializeIgnore] public virtual ICollection<Link<Library, Provider>> LibraryLinks { get; set; }
[SerializeIgnore] public virtual ICollection<MetadataID> MetadataLinks { get; set; } [SerializeIgnore] public virtual ICollection<MetadataID> MetadataLinks { get; set; }
#endif #endif
public ProviderID() { } public Provider() { }
public ProviderID(string name, string logo) public Provider(string name, string logo)
{ {
Slug = Utility.ToSlug(name); Slug = Utility.ToSlug(name);
Name = name; Name = name;
Logo = logo; Logo = logo;
} }
public ProviderID(int id, string name, string logo) public Provider(int id, string name, string logo)
{ {
ID = id; ID = id;
Slug = Utility.ToSlug(name); Slug = Utility.ToSlug(name);

View File

@ -109,18 +109,18 @@ namespace Kyoo.Models
if (!ep.Show.IsMovie) if (!ep.Show.IsMovie)
{ {
if (ep.EpisodeNumber > 1) if (ep.EpisodeNumber > 1)
previous = await library.GetEpisode(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 1); previous = await library.Get(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 1);
else if (ep.SeasonNumber > 1) else if (ep.SeasonNumber > 1)
{ {
int count = await library.GetEpisodesCount(x => x.ShowID == ep.ShowID int count = await library.GetCount<Episode>(x => x.ShowID == ep.ShowID
&& x.SeasonNumber == ep.SeasonNumber - 1); && x.SeasonNumber == ep.SeasonNumber - 1);
previous = await library.GetEpisode(ep.ShowID, ep.SeasonNumber - 1, count); previous = await library.Get(ep.ShowID, ep.SeasonNumber - 1, count);
} }
if (ep.EpisodeNumber >= await library.GetEpisodesCount(x => x.SeasonID == ep.SeasonID)) if (ep.EpisodeNumber >= await library.GetCount<Episode>(x => x.SeasonID == ep.SeasonID))
next = await library.GetEpisode(ep.ShowID, ep.SeasonNumber + 1, 1); next = await library.Get(ep.ShowID, ep.SeasonNumber + 1, 1);
else else
next = await library.GetEpisode(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1); next = await library.Get(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1);
} }
return new WatchItem(ep.ID, return new WatchItem(ep.ID,

View File

@ -25,6 +25,8 @@ namespace Kyoo.Controllers
Database = database; Database = database;
} }
public Type RepositoryType => typeof(T);
public virtual void Dispose() public virtual void Dispose()
{ {
Database.Dispose(); Database.Dispose();

@ -1 +1 @@
Subproject commit ddf3337acb766ae9e0987044ae38130d94fe60ad Subproject commit da35a725a3e47db0994a697595aec4a10a4886e3

View File

@ -8,10 +8,10 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public class ProviderRepository : LocalRepository<ProviderID>, IProviderRepository public class ProviderRepository : LocalRepository<Provider>, IProviderRepository
{ {
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
protected override Expression<Func<ProviderID, object>> DefaultSort => x => x.Slug; protected override Expression<Func<Provider, object>> DefaultSort => x => x.Slug;
public ProviderRepository(DatabaseContext database) : base(database) public ProviderRepository(DatabaseContext database) : base(database)
@ -19,7 +19,7 @@ namespace Kyoo.Controllers
_database = database; _database = database;
} }
public override async Task<ICollection<ProviderID>> Search(string query) public override async Task<ICollection<Provider>> Search(string query)
{ {
return await _database.Providers return await _database.Providers
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%")) .Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
@ -28,7 +28,7 @@ namespace Kyoo.Controllers
.ToListAsync(); .ToListAsync();
} }
public override async Task<ProviderID> Create(ProviderID obj) public override async Task<Provider> Create(Provider obj)
{ {
await base.Create(obj); await base.Create(obj);
_database.Entry(obj).State = EntityState.Added; _database.Entry(obj).State = EntityState.Added;
@ -36,7 +36,7 @@ namespace Kyoo.Controllers
return obj; return obj;
} }
public override async Task Delete(ProviderID obj) public override async Task Delete(Provider obj)
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));

View File

@ -92,7 +92,7 @@ namespace Kyoo.Controllers
await DownloadImage(episode.Thumb, localPath, $"The thumbnail of {episode.Slug}"); await DownloadImage(episode.Thumb, localPath, $"The thumbnail of {episode.Slug}");
} }
public async Task Validate(ProviderID provider, bool alwaysDownload) public async Task Validate(Provider provider, bool alwaysDownload)
{ {
if (provider.Logo == null) if (provider.Logo == null)
return; return;
@ -145,7 +145,7 @@ namespace Kyoo.Controllers
return Task.FromResult(thumbPath.StartsWith(_peoplePath) ? thumbPath : null); return Task.FromResult(thumbPath.StartsWith(_peoplePath) ? thumbPath : null);
} }
public Task<string> GetProviderLogo(ProviderID provider) public Task<string> GetProviderLogo(Provider provider)
{ {
if (provider == null) if (provider == null)
throw new ArgumentNullException(nameof(provider)); throw new ArgumentNullException(nameof(provider));

View File

@ -27,7 +27,7 @@ namespace Kyoo
public DbSet<Genre> Genres { get; set; } public DbSet<Genre> Genres { get; set; }
public DbSet<People> People { get; set; } public DbSet<People> People { get; set; }
public DbSet<Studio> Studios { get; set; } public DbSet<Studio> Studios { get; set; }
public DbSet<ProviderID> Providers { get; set; } public DbSet<Provider> Providers { get; set; }
public DbSet<MetadataID> MetadataIds { get; set; } public DbSet<MetadataID> MetadataIds { get; set; }
public DbSet<PeopleRole> PeopleRoles { get; set; } public DbSet<PeopleRole> PeopleRoles { get; set; }
@ -74,17 +74,17 @@ namespace Kyoo
.Property(t => t.IsForced) .Property(t => t.IsForced)
.ValueGeneratedNever(); .ValueGeneratedNever();
modelBuilder.Entity<ProviderID>() modelBuilder.Entity<Provider>()
.HasMany(x => x.Libraries) .HasMany(x => x.Libraries)
.WithMany(x => x.Providers) .WithMany(x => x.Providers)
.UsingEntity<Link<Library, ProviderID>>( .UsingEntity<Link<Library, Provider>>(
y => y y => y
.HasOne(x => x.First) .HasOne(x => x.First)
.WithMany(x => x.ProviderLinks), .WithMany(x => x.ProviderLinks),
y => y y => y
.HasOne(x => x.Second) .HasOne(x => x.Second)
.WithMany(x => x.LibraryLinks), .WithMany(x => x.LibraryLinks),
y => y.HasKey(Link<Library, ProviderID>.PrimaryKey)); y => y.HasKey(Link<Library, Provider>.PrimaryKey));
modelBuilder.Entity<Collection>() modelBuilder.Entity<Collection>()
.HasMany(x => x.Libraries) .HasMany(x => x.Libraries)
@ -160,7 +160,7 @@ namespace Kyoo
modelBuilder.Entity<Genre>().Property(x => x.Slug).IsRequired(); modelBuilder.Entity<Genre>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Library>().Property(x => x.Slug).IsRequired(); modelBuilder.Entity<Library>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<People>().Property(x => x.Slug).IsRequired(); modelBuilder.Entity<People>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<ProviderID>().Property(x => x.Slug).IsRequired(); modelBuilder.Entity<Provider>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Show>().Property(x => x.Slug).IsRequired(); modelBuilder.Entity<Show>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Studio>().Property(x => x.Slug).IsRequired(); modelBuilder.Entity<Studio>().Property(x => x.Slug).IsRequired();
@ -182,7 +182,7 @@ namespace Kyoo
modelBuilder.Entity<Studio>() modelBuilder.Entity<Studio>()
.HasIndex(x => x.Slug) .HasIndex(x => x.Slug)
.IsUnique(); .IsUnique();
modelBuilder.Entity<ProviderID>() modelBuilder.Entity<Provider>()
.HasIndex(x => x.Slug) .HasIndex(x => x.Slug)
.IsUnique(); .IsUnique();
modelBuilder.Entity<Season>() modelBuilder.Entity<Season>()

View File

@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Kyoo.Models.DatabaseMigrations.Internal namespace Kyoo.Models.DatabaseMigrations.Internal
{ {
[DbContext(typeof(DatabaseContext))] [DbContext(typeof(DatabaseContext))]
[Migration("20210325184215_Initial")] [Migration("20210417232515_Initial")]
partial class Initial partial class Initial
{ {
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -179,7 +179,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.ToTable("Link<Library, Collection>"); b.ToTable("Link<Library, Collection>");
}); });
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b => modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Provider>", b =>
{ {
b.Property<int>("FirstID") b.Property<int>("FirstID")
.HasColumnType("integer"); .HasColumnType("integer");
@ -191,7 +191,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.HasIndex("SecondID"); b.HasIndex("SecondID");
b.ToTable("Link<Library, ProviderID>"); b.ToTable("Link<Library, Provider>");
}); });
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b => modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b =>
@ -320,7 +320,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.ToTable("PeopleRoles"); b.ToTable("PeopleRoles");
}); });
modelBuilder.Entity("Kyoo.Models.ProviderID", b => modelBuilder.Entity("Kyoo.Models.Provider", b =>
{ {
b.Property<int>("ID") b.Property<int>("ID")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -563,7 +563,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Navigation("Second"); b.Navigation("Second");
}); });
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b => modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Provider>", b =>
{ {
b.HasOne("Kyoo.Models.Library", "First") b.HasOne("Kyoo.Models.Library", "First")
.WithMany("ProviderLinks") .WithMany("ProviderLinks")
@ -571,7 +571,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Kyoo.Models.ProviderID", "Second") b.HasOne("Kyoo.Models.Provider", "Second")
.WithMany("LibraryLinks") .WithMany("LibraryLinks")
.HasForeignKey("SecondID") .HasForeignKey("SecondID")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@ -632,7 +632,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
.HasForeignKey("PeopleID") .HasForeignKey("PeopleID")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
b.HasOne("Kyoo.Models.ProviderID", "Provider") b.HasOne("Kyoo.Models.Provider", "Provider")
.WithMany("MetadataLinks") .WithMany("MetadataLinks")
.HasForeignKey("ProviderID") .HasForeignKey("ProviderID")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@ -744,7 +744,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Navigation("Roles"); b.Navigation("Roles");
}); });
modelBuilder.Entity("Kyoo.Models.ProviderID", b => modelBuilder.Entity("Kyoo.Models.Provider", b =>
{ {
b.Navigation("LibraryLinks"); b.Navigation("LibraryLinks");

View File

@ -128,7 +128,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
}); });
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Link<Library, ProviderID>", name: "Link<Library, Provider>",
columns: table => new columns: table => new
{ {
FirstID = table.Column<int>(type: "integer", nullable: false), FirstID = table.Column<int>(type: "integer", nullable: false),
@ -136,15 +136,15 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
}, },
constraints: table => constraints: table =>
{ {
table.PrimaryKey("PK_Link<Library, ProviderID>", x => new { x.FirstID, x.SecondID }); table.PrimaryKey("PK_Link<Library, Provider>", x => new { x.FirstID, x.SecondID });
table.ForeignKey( table.ForeignKey(
name: "FK_Link<Library, ProviderID>_Libraries_FirstID", name: "FK_Link<Library, Provider>_Libraries_FirstID",
column: x => x.FirstID, column: x => x.FirstID,
principalTable: "Libraries", principalTable: "Libraries",
principalColumn: "ID", principalColumn: "ID",
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
table.ForeignKey( table.ForeignKey(
name: "FK_Link<Library, ProviderID>_Providers_SecondID", name: "FK_Link<Library, Provider>_Providers_SecondID",
column: x => x.SecondID, column: x => x.SecondID,
principalTable: "Providers", principalTable: "Providers",
principalColumn: "ID", principalColumn: "ID",
@ -459,8 +459,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
column: "SecondID"); column: "SecondID");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Link<Library, ProviderID>_SecondID", name: "IX_Link<Library, Provider>_SecondID",
table: "Link<Library, ProviderID>", table: "Link<Library, Provider>",
column: "SecondID"); column: "SecondID");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
@ -559,7 +559,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
name: "Link<Library, Collection>"); name: "Link<Library, Collection>");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Link<Library, ProviderID>"); name: "Link<Library, Provider>");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Link<Library, Show>"); name: "Link<Library, Show>");

View File

@ -177,7 +177,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.ToTable("Link<Library, Collection>"); b.ToTable("Link<Library, Collection>");
}); });
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b => modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Provider>", b =>
{ {
b.Property<int>("FirstID") b.Property<int>("FirstID")
.HasColumnType("integer"); .HasColumnType("integer");
@ -189,7 +189,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.HasIndex("SecondID"); b.HasIndex("SecondID");
b.ToTable("Link<Library, ProviderID>"); b.ToTable("Link<Library, Provider>");
}); });
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b => modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Show>", b =>
@ -318,7 +318,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.ToTable("PeopleRoles"); b.ToTable("PeopleRoles");
}); });
modelBuilder.Entity("Kyoo.Models.ProviderID", b => modelBuilder.Entity("Kyoo.Models.Provider", b =>
{ {
b.Property<int>("ID") b.Property<int>("ID")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -561,7 +561,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Navigation("Second"); b.Navigation("Second");
}); });
modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.ProviderID>", b => modelBuilder.Entity("Kyoo.Models.Link<Kyoo.Models.Library, Kyoo.Models.Provider>", b =>
{ {
b.HasOne("Kyoo.Models.Library", "First") b.HasOne("Kyoo.Models.Library", "First")
.WithMany("ProviderLinks") .WithMany("ProviderLinks")
@ -569,7 +569,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("Kyoo.Models.ProviderID", "Second") b.HasOne("Kyoo.Models.Provider", "Second")
.WithMany("LibraryLinks") .WithMany("LibraryLinks")
.HasForeignKey("SecondID") .HasForeignKey("SecondID")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@ -630,7 +630,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
.HasForeignKey("PeopleID") .HasForeignKey("PeopleID")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
b.HasOne("Kyoo.Models.ProviderID", "Provider") b.HasOne("Kyoo.Models.Provider", "Provider")
.WithMany("MetadataLinks") .WithMany("MetadataLinks")
.HasForeignKey("ProviderID") .HasForeignKey("ProviderID")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
@ -742,7 +742,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
b.Navigation("Roles"); b.Navigation("Roles");
}); });
modelBuilder.Entity("Kyoo.Models.ProviderID", b => modelBuilder.Entity("Kyoo.Models.Provider", b =>
{ {
b.Navigation("LibraryLinks"); b.Navigation("LibraryLinks");

View File

@ -147,6 +147,20 @@ namespace Kyoo
}); });
// TODO Add custom method to the service container and expose those methods to the plugin
// TODO Add for example a AddRepository that will automatically register the complex interface, the IRepository<T> and the IBaseRepository
services.AddScoped<IBaseRepository, LibraryRepository>();
services.AddScoped<IBaseRepository, LibraryItemRepository>();
services.AddScoped<IBaseRepository, CollectionRepository>();
services.AddScoped<IBaseRepository, ShowRepository>();
services.AddScoped<IBaseRepository, SeasonRepository>();
services.AddScoped<IBaseRepository, EpisodeRepository>();
services.AddScoped<IBaseRepository, TrackRepository>();
services.AddScoped<IBaseRepository, PeopleRepository>();
services.AddScoped<IBaseRepository, StudioRepository>();
services.AddScoped<IBaseRepository, GenreRepository>();
services.AddScoped<IBaseRepository, ProviderRepository>();
services.AddScoped<ILibraryRepository, LibraryRepository>(); services.AddScoped<ILibraryRepository, LibraryRepository>();
services.AddScoped<ILibraryItemRepository, LibraryItemRepository>(); services.AddScoped<ILibraryItemRepository, LibraryItemRepository>();
services.AddScoped<ICollectionRepository, CollectionRepository>(); services.AddScoped<ICollectionRepository, CollectionRepository>();

View File

@ -33,7 +33,7 @@ namespace Kyoo.Controllers
{ {
using IServiceScope serviceScope = _serviceProvider.CreateScope(); using IServiceScope serviceScope = _serviceProvider.CreateScope();
await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>(); await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
return (await libraryManager!.GetLibraries()).Select(x => x.Slug); return (await libraryManager!.GetAll<Library>()).Select(x => x.Slug);
} }
public int? Progress() public int? Progress()
@ -58,23 +58,23 @@ namespace Kyoo.Controllers
using IServiceScope serviceScope = _serviceProvider.CreateScope(); using IServiceScope serviceScope = _serviceProvider.CreateScope();
await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>(); await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
foreach (Show show in await libraryManager!.GetShows()) foreach (Show show in await libraryManager!.GetAll<Show>())
if (!Directory.Exists(show.Path)) if (!Directory.Exists(show.Path))
await libraryManager.DeleteShow(show); await libraryManager.Delete(show);
ICollection<Episode> episodes = await libraryManager.GetEpisodes(); ICollection<Episode> episodes = await libraryManager.GetAll<Episode>();
foreach (Episode episode in episodes) foreach (Episode episode in episodes)
if (!File.Exists(episode.Path)) if (!File.Exists(episode.Path))
await libraryManager.DeleteEpisode(episode); await libraryManager.Delete(episode);
ICollection<Track> tracks = await libraryManager.GetTracks(); ICollection<Track> tracks = await libraryManager.GetAll<Track>();
foreach (Track track in tracks) foreach (Track track in tracks)
if (!File.Exists(track.Path)) if (!File.Exists(track.Path))
await libraryManager.DeleteTrack(track); await libraryManager.Delete(track);
ICollection<Library> libraries = argument == null ICollection<Library> libraries = argument == null
? await libraryManager.GetLibraries() ? await libraryManager.GetAll<Library>()
: new [] { await libraryManager.GetLibrary(argument)}; : new [] { await libraryManager.Get<Library>(argument)};
if (argument != null && libraries.First() == null) if (argument != null && libraries.First() == null)
throw new ArgumentException($"No library found with the name {argument}"); throw new ArgumentException($"No library found with the name {argument}");
@ -160,7 +160,7 @@ namespace Kyoo.Controllers
} }
string episodePath = match.Groups["Episode"].Value; string episodePath = match.Groups["Episode"].Value;
Episode episode = await libraryManager!.GetEpisode(x => x.Path.StartsWith(episodePath)); Episode episode = await libraryManager!.Get<Episode>(x => x.Path.StartsWith(episodePath));
if (episode == null) if (episode == null)
{ {
@ -180,7 +180,7 @@ namespace Kyoo.Controllers
Episode = episode Episode = episode
}; };
await libraryManager.RegisterTrack(track); await libraryManager.Create(track);
Console.WriteLine($"Registering subtitle at: {path}."); Console.WriteLine($"Registering subtitle at: {path}.");
} }
@ -215,7 +215,7 @@ namespace Kyoo.Controllers
bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1; bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1;
Show show = await GetShow(libraryManager, showName, showPath, isMovie, library); Show show = await GetShow(libraryManager, showName, showPath, isMovie, library);
if (isMovie) if (isMovie)
await libraryManager!.RegisterEpisode(await GetMovie(show, path)); await libraryManager!.Create(await GetMovie(show, path));
else else
{ {
Season season = await GetSeason(libraryManager, show, seasonNumber, library); Season season = await GetSeason(libraryManager, show, seasonNumber, library);
@ -226,7 +226,7 @@ namespace Kyoo.Controllers
absoluteNumber, absoluteNumber,
path, path,
library); library);
await libraryManager!.RegisterEpisode(episode); await libraryManager!.Create(episode);
} }
await libraryManager.AddShowLink(show, library, collection); await libraryManager.AddShowLink(show, library, collection);
@ -250,19 +250,19 @@ namespace Kyoo.Controllers
{ {
if (string.IsNullOrEmpty(collectionName)) if (string.IsNullOrEmpty(collectionName))
return null; return null;
Collection collection = await libraryManager.GetCollection(Utility.ToSlug(collectionName)); Collection collection = await libraryManager.Get<Collection>(Utility.ToSlug(collectionName));
if (collection != null) if (collection != null)
return collection; return collection;
collection = await _metadataProvider.GetCollectionFromName(collectionName, library); collection = await _metadataProvider.GetCollectionFromName(collectionName, library);
try try
{ {
await libraryManager.RegisterCollection(collection); await libraryManager.Create(collection);
return collection; return collection;
} }
catch (DuplicatedItemException) catch (DuplicatedItemException)
{ {
return await libraryManager.GetCollection(collection.Slug); return await libraryManager.Get<Collection>(collection.Slug);
} }
} }
@ -272,7 +272,7 @@ namespace Kyoo.Controllers
bool isMovie, bool isMovie,
Library library) Library library)
{ {
Show old = await libraryManager.GetShow(x => x.Path == showPath); Show old = await libraryManager.Get<Show>(x => x.Path == showPath);
if (old != null) if (old != null)
{ {
await libraryManager.Load(old, x => x.ExternalIDs); await libraryManager.Load(old, x => x.ExternalIDs);
@ -284,18 +284,18 @@ namespace Kyoo.Controllers
try try
{ {
show = await libraryManager.RegisterShow(show); show = await libraryManager.Create(show);
} }
catch (DuplicatedItemException) catch (DuplicatedItemException)
{ {
old = await libraryManager.GetShow(show.Slug); old = await libraryManager.Get<Show>(show.Slug);
if (old.Path == showPath) if (old.Path == showPath)
{ {
await libraryManager.Load(old, x => x.ExternalIDs); await libraryManager.Load(old, x => x.ExternalIDs);
return old; return old;
} }
show.Slug += $"-{show.StartYear}"; show.Slug += $"-{show.StartYear}";
await libraryManager.RegisterShow(show); await libraryManager.Create(show);
} }
await _thumbnailsManager.Validate(show); await _thumbnailsManager.Validate(show);
return show; return show;
@ -308,18 +308,18 @@ namespace Kyoo.Controllers
{ {
if (seasonNumber == -1) if (seasonNumber == -1)
return default; return default;
Season season = await libraryManager.GetSeason(show.Slug, seasonNumber); Season season = await libraryManager.Get(show.Slug, seasonNumber);
if (season == null) if (season == null)
{ {
season = await _metadataProvider.GetSeason(show, seasonNumber, library); season = await _metadataProvider.GetSeason(show, seasonNumber, library);
try try
{ {
await libraryManager.RegisterSeason(season); await libraryManager.Create(season);
await _thumbnailsManager.Validate(season); await _thumbnailsManager.Validate(season);
} }
catch (DuplicatedItemException) catch (DuplicatedItemException)
{ {
season = await libraryManager.GetSeason(show.Slug, season.SeasonNumber); season = await libraryManager.Get(show.Slug, season.SeasonNumber);
} }
} }
season.Show = show; season.Show = show;

View File

@ -45,22 +45,22 @@ namespace Kyoo.Tasks
case "show": case "show":
case "shows": case "shows":
Show show = await (int.TryParse(slug, out id) Show show = await (int.TryParse(slug, out id)
? _library!.GetShow(id) ? _library!.Get<Show>(id)
: _library!.GetShow(slug)); : _library!.Get<Show>(slug));
await ExtractShow(show, thumbs, subs, token); await ExtractShow(show, thumbs, subs, token);
break; break;
case "season": case "season":
case "seasons": case "seasons":
Season season = await (int.TryParse(slug, out id) Season season = await (int.TryParse(slug, out id)
? _library!.GetSeason(id) ? _library!.Get<Season>(id)
: _library!.GetSeason(slug)); : _library!.Get<Season>(slug));
await ExtractSeason(season, thumbs, subs, token); await ExtractSeason(season, thumbs, subs, token);
break; break;
case "episode": case "episode":
case "episodes": case "episodes":
Episode episode = await (int.TryParse(slug, out id) Episode episode = await (int.TryParse(slug, out id)
? _library!.GetEpisode(id) ? _library!.Get<Episode>(id)
: _library!.GetEpisode(slug)); : _library!.Get<Episode>(slug));
await ExtractEpisode(episode, thumbs, subs); await ExtractEpisode(episode, thumbs, subs);
break; break;
} }
@ -105,7 +105,7 @@ namespace Kyoo.Tasks
.Where(x => x.Type != StreamType.Attachment) .Where(x => x.Type != StreamType.Attachment)
.Concat(episode.Tracks.Where(x => x.IsExternal)) .Concat(episode.Tracks.Where(x => x.IsExternal))
.ToList(); .ToList();
await _library.EditEpisode(episode, false); await _library.Edit(episode, false);
} }
} }

View File

@ -35,12 +35,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.Collections.Any(y => y.ID == id)), ApiHelper.ParseWhere<Show>(where, x => x.Collections.Any(y => y.ID == id)),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetCollection(id) == null) if (!resources.Any() && await _libraryManager.Get<Collection>(id) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -61,12 +61,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.Collections.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Show>(where, x => x.Collections.Any(y => y.Slug == slug)),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetCollection(slug) == null) if (!resources.Any() && await _libraryManager.Get<Collection>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -87,12 +87,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Library> resources = await _libraryManager.GetLibraries( ICollection<Library> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Library>(where, x => x.Collections.Any(y => y.ID == id)), ApiHelper.ParseWhere<Library>(where, x => x.Collections.Any(y => y.ID == id)),
new Sort<Library>(sortBy), new Sort<Library>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetCollection(id) == null) if (!resources.Any() && await _libraryManager.Get<Collection>(id) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -113,12 +113,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Library> resources = await _libraryManager.GetLibraries( ICollection<Library> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Library>(where, x => x.Collections.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Library>(where, x => x.Collections.Any(y => y.Slug == slug)),
new Sort<Library>(sortBy), new Sort<Library>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetCollection(slug) == null) if (!resources.Any() && await _libraryManager.Get<Collection>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }

View File

@ -35,42 +35,42 @@ namespace Kyoo.Api
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Show>> GetShow(int episodeID) public async Task<ActionResult<Show>> GetShow(int episodeID)
{ {
return await _libraryManager.GetShow(x => x.Episodes.Any(y => y.ID == episodeID)); return await _libraryManager.Get<Show>(x => x.Episodes.Any(y => y.ID == episodeID));
} }
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/show")] [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/show")]
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Show>> GetShow(string showSlug) public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber, int episodeNumber)
{ {
return await _libraryManager.GetShow(showSlug); return await _libraryManager.Get<Show>(showSlug);
} }
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/show")] [HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/show")]
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Show>> GetShow(int showID, int _) public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber, int episodeNumber)
{ {
return await _libraryManager.GetShow(showID); return await _libraryManager.Get<Show>(showID);
} }
[HttpGet("{episodeID:int}/season")] [HttpGet("{episodeID:int}/season")]
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Season>> GetSeason(int episodeID) public async Task<ActionResult<Season>> GetSeason(int episodeID)
{ {
return await _libraryManager.GetSeason(x => x.Episodes.Any(y => y.ID == episodeID)); return await _libraryManager.Get<Season>(x => x.Episodes.Any(y => y.ID == episodeID));
} }
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")] [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")]
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNuber) public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
{ {
return await _libraryManager.GetSeason(showSlug, seasonNuber); return await _libraryManager.Get(showSlug, seasonNumber);
} }
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")] [HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber) public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber)
{ {
return await _libraryManager.GetSeason(showID, seasonNumber); return await _libraryManager.Get(showID, seasonNumber);
} }
[HttpGet("{episodeID:int}/track")] [HttpGet("{episodeID:int}/track")]
@ -84,12 +84,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Track> resources = await _libraryManager.GetTracks( ICollection<Track> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Track>(where, x => x.Episode.ID == episodeID), ApiHelper.ParseWhere<Track>(where, x => x.Episode.ID == episodeID),
new Sort<Track>(sortBy), new Sort<Track>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetEpisode(episodeID) == null) if (!resources.Any() && await _libraryManager.Get<Episode>(episodeID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -112,14 +112,14 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Track> resources = await _libraryManager.GetTracks( ICollection<Track> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Track>(where, x => x.Episode.ShowID == showID ApiHelper.ParseWhere<Track>(where, x => x.Episode.ShowID == showID
&& x.Episode.SeasonNumber == seasonNumber && x.Episode.SeasonNumber == seasonNumber
&& x.Episode.EpisodeNumber == episodeNumber), && x.Episode.EpisodeNumber == episodeNumber),
new Sort<Track>(sortBy), new Sort<Track>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetEpisode(showID, seasonNumber, episodeNumber) == null) if (!resources.Any() && await _libraryManager.Get(showID, seasonNumber, episodeNumber) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -142,13 +142,14 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Track> resources = await _libraryManager.GetTracks(ApiHelper.ParseWhere<Track>(where, x => x.Episode.Show.Slug == showSlug ICollection<Track> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Track>(where, x => x.Episode.Show.Slug == showSlug
&& x.Episode.SeasonNumber == seasonNumber && x.Episode.SeasonNumber == seasonNumber
&& x.Episode.EpisodeNumber == episodeNumber), && x.Episode.EpisodeNumber == episodeNumber),
new Sort<Track>(sortBy), new Sort<Track>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber) == null) if (!resources.Any() && await _libraryManager.Get(showSlug, seasonNumber, episodeNumber) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -162,7 +163,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetThumb(int id) public async Task<IActionResult> GetThumb(int id)
{ {
Episode episode = await _libraryManager.GetEpisode(id); Episode episode = await _libraryManager.Get<Episode>(id);
if (episode == null) if (episode == null)
return NotFound(); return NotFound();
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode)); return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
@ -172,7 +173,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetThumb(string slug) public async Task<IActionResult> GetThumb(string slug)
{ {
Episode episode = await _libraryManager.GetEpisode(slug); Episode episode = await _libraryManager.Get<Episode>(slug);
if (episode == null) if (episode == null)
return NotFound(); return NotFound();
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode)); return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));

View File

@ -36,12 +36,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.Genres.Any(y => y.ID == id)), ApiHelper.ParseWhere<Show>(where, x => x.Genres.Any(y => y.ID == id)),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetGenre(id) == null) if (!resources.Any() && await _libraryManager.Get<Genre>(id) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -62,12 +62,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.Genres.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Show>(where, x => x.Genres.Any(y => y.Slug == slug)),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetGenre(slug) == null) if (!resources.Any() && await _libraryManager.Get<Genre>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }

View File

@ -47,12 +47,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.Libraries.Any(y => y.ID == id)), ApiHelper.ParseWhere<Show>(where, x => x.Libraries.Any(y => y.ID == id)),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetLibrary(id) == null) if (!resources.Any() && await _libraryManager.Get<Library>(id) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -73,12 +73,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.Libraries.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Show>(where, x => x.Libraries.Any(y => y.Slug == slug)),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetLibrary(slug) == null) if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -99,12 +99,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Collection> resources = await _libraryManager.GetCollections( ICollection<Collection> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Collection>(where, x => x.Libraries.Any(y => y.ID == id)), ApiHelper.ParseWhere<Collection>(where, x => x.Libraries.Any(y => y.ID == id)),
new Sort<Collection>(sortBy), new Sort<Collection>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetLibrary(id) == null) if (!resources.Any() && await _libraryManager.Get<Library>(id) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -125,12 +125,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Collection> resources = await _libraryManager.GetCollections( ICollection<Collection> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Collection>(where, x => x.Libraries.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Collection>(where, x => x.Libraries.Any(y => y.Slug == slug)),
new Sort<Collection>(sortBy), new Sort<Collection>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetLibrary(slug) == null) if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -156,7 +156,7 @@ namespace Kyoo.Api
new Sort<LibraryItem>(sortBy), new Sort<LibraryItem>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetLibrary(id) == null) if (!resources.Any() && await _libraryManager.Get<Library>(id) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -182,7 +182,7 @@ namespace Kyoo.Api
new Sort<LibraryItem>(sortBy), new Sort<LibraryItem>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetLibrary(slug) == null) if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }

View File

@ -90,7 +90,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetPeopleIcon(int id) public async Task<IActionResult> GetPeopleIcon(int id)
{ {
People people = await _libraryManager.GetPeople(id); People people = await _libraryManager.Get<People>(id);
return _files.FileResult(await _thumbs.GetPeoplePoster(people)); return _files.FileResult(await _thumbs.GetPeoplePoster(people));
} }
@ -98,7 +98,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetPeopleIcon(string slug) public async Task<IActionResult> GetPeopleIcon(string slug)
{ {
People people = await _libraryManager.GetPeople(slug); People people = await _libraryManager.Get<People>(slug);
return _files.FileResult(await _thumbs.GetPeoplePoster(people)); return _files.FileResult(await _thumbs.GetPeoplePoster(people));
} }
} }

View File

@ -11,7 +11,7 @@ namespace Kyoo.Api
[Route("api/provider")] [Route("api/provider")]
[Route("api/providers")] [Route("api/providers")]
[ApiController] [ApiController]
public class ProviderAPI : CrudApi<ProviderID> public class ProviderAPI : CrudApi<Provider>
{ {
private readonly IThumbnailsManager _thumbnails; private readonly IThumbnailsManager _thumbnails;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
@ -32,7 +32,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetLogo(int id) public async Task<IActionResult> GetLogo(int id)
{ {
ProviderID provider = await _libraryManager.GetProvider(id); Provider provider = await _libraryManager.Get<Provider>(id);
return _files.FileResult(await _thumbnails.GetProviderLogo(provider)); return _files.FileResult(await _thumbnails.GetProviderLogo(provider));
} }
@ -40,7 +40,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetLogo(string slug) public async Task<IActionResult> GetLogo(string slug)
{ {
ProviderID provider = await _libraryManager.GetProvider(slug); Provider provider = await _libraryManager.Get<Provider>(slug);
return _files.FileResult(await _thumbnails.GetProviderLogo(provider)); return _files.FileResult(await _thumbnails.GetProviderLogo(provider));
} }
} }

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Api namespace Kyoo.Api
{ {
[Route("api/search")] [Route("api/search/{query}")]
[ApiController] [ApiController]
public class SearchApi : ControllerBase public class SearchApi : ControllerBase
{ {
@ -18,67 +18,67 @@ namespace Kyoo.Api
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{query}")] [HttpGet]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<ActionResult<SearchResult>> Search(string query) public async Task<ActionResult<SearchResult>> Search(string query)
{ {
return new SearchResult return new SearchResult
{ {
Query = query, Query = query,
Collections = await _libraryManager.SearchCollections(query), Collections = await _libraryManager.Search<Collection>(query),
Shows = await _libraryManager.SearchShows(query), Shows = await _libraryManager.Search<Show>(query),
Episodes = await _libraryManager.SearchEpisodes(query), Episodes = await _libraryManager.Search<Episode>(query),
People = await _libraryManager.SearchPeople(query), People = await _libraryManager.Search<People>(query),
Genres = await _libraryManager.SearchGenres(query), Genres = await _libraryManager.Search<Genre>(query),
Studios = await _libraryManager.SearchStudios(query) Studios = await _libraryManager.Search<Studio>(query)
}; };
} }
[HttpGet("{query}/collection")] [HttpGet("collection")]
[HttpGet("{query}/collections")] [HttpGet("collections")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public Task<ICollection<Collection>> SearchCollections(string query) public Task<ICollection<Collection>> SearchCollections(string query)
{ {
return _libraryManager.SearchCollections(query); return _libraryManager.Search<Collection>(query);
} }
[HttpGet("{query}/show")] [HttpGet("show")]
[HttpGet("{query}/shows")] [HttpGet("shows")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public Task<ICollection<Show>> SearchShows(string query) public Task<ICollection<Show>> SearchShows(string query)
{ {
return _libraryManager.SearchShows(query); return _libraryManager.Search<Show>(query);
} }
[HttpGet("{query}/episode")] [HttpGet("episode")]
[HttpGet("{query}/episodes")] [HttpGet("episodes")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public Task<ICollection<Episode>> SearchEpisodes(string query) public Task<ICollection<Episode>> SearchEpisodes(string query)
{ {
return _libraryManager.SearchEpisodes(query); return _libraryManager.Search<Episode>(query);
} }
[HttpGet("{query}/people")] [HttpGet("people")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public Task<ICollection<People>> SearchPeople(string query) public Task<ICollection<People>> SearchPeople(string query)
{ {
return _libraryManager.SearchPeople(query); return _libraryManager.Search<People>(query);
} }
[HttpGet("{query}/genre")] [HttpGet("genre")]
[HttpGet("{query}/genres")] [HttpGet("genres")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public Task<ICollection<Genre>> SearchGenres(string query) public Task<ICollection<Genre>> SearchGenres(string query)
{ {
return _libraryManager.SearchGenres(query); return _libraryManager.Search<Genre>(query);
} }
[HttpGet("{query}/studio")] [HttpGet("studio")]
[HttpGet("{query}/studios")] [HttpGet("studios")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public Task<ICollection<Studio>> SearchStudios(string query) public Task<ICollection<Studio>> SearchStudios(string query)
{ {
return _libraryManager.SearchStudios(query); return _libraryManager.Search<Studio>(query);
} }
} }
} }

View File

@ -42,12 +42,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Episode> resources = await _libraryManager.GetEpisodes( ICollection<Episode> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Episode>(where, x => x.SeasonID == seasonID), ApiHelper.ParseWhere<Episode>(where, x => x.SeasonID == seasonID),
new Sort<Episode>(sortBy), new Sort<Episode>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetSeason(seasonID) == null) if (!resources.Any() && await _libraryManager.Get<Season>(seasonID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -69,13 +69,13 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Episode> resources = await _libraryManager.GetEpisodes( ICollection<Episode> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Episode>(where, x => x.Show.Slug == showSlug ApiHelper.ParseWhere<Episode>(where, x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber), && x.SeasonNumber == seasonNumber),
new Sort<Episode>(sortBy), new Sort<Episode>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetSeason(showSlug, seasonNumber) == null) if (!resources.Any() && await _libraryManager.Get(showSlug, seasonNumber) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -97,12 +97,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Episode> resources = await _libraryManager.GetEpisodes( ICollection<Episode> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Episode>(where, x => x.ShowID == showID && x.SeasonNumber == seasonNumber), ApiHelper.ParseWhere<Episode>(where, x => x.ShowID == showID && x.SeasonNumber == seasonNumber),
new Sort<Episode>(sortBy), new Sort<Episode>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetSeason(showID, seasonNumber) == null) if (!resources.Any() && await _libraryManager.Get(showID, seasonNumber) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -116,28 +116,28 @@ namespace Kyoo.Api
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Show>> GetShow(int seasonID) public async Task<ActionResult<Show>> GetShow(int seasonID)
{ {
return await _libraryManager.GetShow(x => x.Seasons.Any(y => y.ID == seasonID)); return await _libraryManager.Get<Show>(x => x.Seasons.Any(y => y.ID == seasonID));
} }
[HttpGet("{showSlug}-s{seasonNumber:int}/show")] [HttpGet("{showSlug}-s{seasonNumber:int}/show")]
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Show>> GetShow(string showSlug, int _) public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber)
{ {
return await _libraryManager.GetShow(showSlug); return await _libraryManager.Get<Show>(showSlug);
} }
[HttpGet("{showID:int}-s{seasonNumber:int}/show")] [HttpGet("{showID:int}-s{seasonNumber:int}/show")]
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Show>> GetShow(int showID, int _) public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber)
{ {
return await _libraryManager.GetShow(showID); return await _libraryManager.Get<Show>(showID);
} }
[HttpGet("{id:int}/thumb")] [HttpGet("{id:int}/thumb")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetThumb(int id) public async Task<IActionResult> GetThumb(int id)
{ {
Season season = await _libraryManager.GetSeason(id); Season season = await _libraryManager.Get<Season>(id);
await _libraryManager.Load(season, x => x.Show); await _libraryManager.Load(season, x => x.Show);
return _files.FileResult(await _thumbs.GetSeasonPoster(season)); return _files.FileResult(await _thumbs.GetSeasonPoster(season));
} }
@ -146,7 +146,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetThumb(string slug) public async Task<IActionResult> GetThumb(string slug)
{ {
Season season = await _libraryManager.GetSeason(slug); Season season = await _libraryManager.Get<Season>(slug);
await _libraryManager.Load(season, x => x.Show); await _libraryManager.Load(season, x => x.Show);
return _files.FileResult(await _thumbs.GetSeasonPoster(season)); return _files.FileResult(await _thumbs.GetSeasonPoster(season));
} }

View File

@ -44,12 +44,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Season> resources = await _libraryManager.GetSeasons( ICollection<Season> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Season>(where, x => x.ShowID == showID), ApiHelper.ParseWhere<Season>(where, x => x.ShowID == showID),
new Sort<Season>(sortBy), new Sort<Season>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(showID) == null) if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -70,12 +70,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Season> resources = await _libraryManager.GetSeasons( ICollection<Season> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Season>(where, x => x.Show.Slug == slug), ApiHelper.ParseWhere<Season>(where, x => x.Show.Slug == slug),
new Sort<Season>(sortBy), new Sort<Season>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(slug) == null) if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -96,12 +96,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Episode> resources = await _libraryManager.GetEpisodes( ICollection<Episode> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Episode>(where, x => x.ShowID == showID), ApiHelper.ParseWhere<Episode>(where, x => x.ShowID == showID),
new Sort<Episode>(sortBy), new Sort<Episode>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(showID) == null) if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -122,12 +122,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Episode> resources = await _libraryManager.GetEpisodes( ICollection<Episode> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Episode>(where, x => x.Show.Slug == slug), ApiHelper.ParseWhere<Episode>(where, x => x.Show.Slug == slug),
new Sort<Episode>(sortBy), new Sort<Episode>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(slug) == null) if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -152,7 +152,7 @@ namespace Kyoo.Api
new Sort<PeopleRole>(sortBy), new Sort<PeopleRole>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(showID) == null) if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -177,7 +177,7 @@ namespace Kyoo.Api
new Sort<PeopleRole>(sortBy), new Sort<PeopleRole>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(slug) == null) if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -198,12 +198,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Genre> resources = await _libraryManager.GetGenres( ICollection<Genre> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Genre>(where, x => x.Shows.Any(y => y.ID == showID)), ApiHelper.ParseWhere<Genre>(where, x => x.Shows.Any(y => y.ID == showID)),
new Sort<Genre>(sortBy), new Sort<Genre>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(showID) == null) if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -224,12 +224,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Genre> resources = await _libraryManager.GetGenres( ICollection<Genre> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Genre>(where, x => x.Shows.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Genre>(where, x => x.Shows.Any(y => y.Slug == slug)),
new Sort<Genre>(sortBy), new Sort<Genre>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(slug) == null) if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -245,7 +245,7 @@ namespace Kyoo.Api
{ {
try try
{ {
return await _libraryManager.GetStudio(x => x.Shows.Any(y => y.ID == showID)); return await _libraryManager.Get<Studio>(x => x.Shows.Any(y => y.ID == showID));
} }
catch (ItemNotFound) catch (ItemNotFound)
{ {
@ -259,7 +259,7 @@ namespace Kyoo.Api
{ {
try try
{ {
return await _libraryManager.GetStudio(x => x.Shows.Any(y => y.Slug == slug)); return await _libraryManager.Get<Studio>(x => x.Shows.Any(y => y.Slug == slug));
} }
catch (ItemNotFound) catch (ItemNotFound)
{ {
@ -278,12 +278,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Library> resources = await _libraryManager.GetLibraries( ICollection<Library> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Library>(where, x => x.Shows.Any(y => y.ID == showID)), ApiHelper.ParseWhere<Library>(where, x => x.Shows.Any(y => y.ID == showID)),
new Sort<Library>(sortBy), new Sort<Library>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(showID) == null) if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -304,12 +304,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Library> resources = await _libraryManager.GetLibraries( ICollection<Library> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Library>(where, x => x.Shows.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Library>(where, x => x.Shows.Any(y => y.Slug == slug)),
new Sort<Library>(sortBy), new Sort<Library>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(slug) == null) if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -330,12 +330,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Collection> resources = await _libraryManager.GetCollections( ICollection<Collection> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Collection>(where, x => x.Shows.Any(y => y.ID == showID)), ApiHelper.ParseWhere<Collection>(where, x => x.Shows.Any(y => y.ID == showID)),
new Sort<Collection>(sortBy), new Sort<Collection>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(showID) == null) if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -356,12 +356,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Collection> resources = await _libraryManager.GetCollections( ICollection<Collection> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Collection>(where, x => x.Shows.Any(y => y.Slug == slug)), ApiHelper.ParseWhere<Collection>(where, x => x.Shows.Any(y => y.Slug == slug)),
new Sort<Collection>(sortBy), new Sort<Collection>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetShow(slug) == null) if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -376,7 +376,7 @@ namespace Kyoo.Api
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<ActionResult<Dictionary<string, string>>> GetFonts(string slug) public async Task<ActionResult<Dictionary<string, string>>> GetFonts(string slug)
{ {
Show show = await _libraryManager.GetShow(slug); Show show = await _libraryManager.Get<Show>(slug);
if (show == null) if (show == null)
return NotFound(); return NotFound();
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments"); string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments");
@ -390,7 +390,7 @@ namespace Kyoo.Api
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<IActionResult> GetFont(string showSlug, string slug) public async Task<IActionResult> GetFont(string showSlug, string slug)
{ {
Show show = await _libraryManager.GetShow(showSlug); Show show = await _libraryManager.Get<Show>(showSlug);
if (show == null) if (show == null)
return NotFound(); return NotFound();
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments", slug); string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments", slug);
@ -401,7 +401,7 @@ namespace Kyoo.Api
[Authorize(Policy = "Read")] [Authorize(Policy = "Read")]
public async Task<IActionResult> GetPoster(string slug) public async Task<IActionResult> GetPoster(string slug)
{ {
Show show = await _libraryManager.GetShow(slug); Show show = await _libraryManager.Get<Show>(slug);
if (show == null) if (show == null)
return NotFound(); return NotFound();
return _files.FileResult(await _thumbs.GetShowPoster(show)); return _files.FileResult(await _thumbs.GetShowPoster(show));
@ -411,7 +411,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetLogo(string slug) public async Task<IActionResult> GetLogo(string slug)
{ {
Show show = await _libraryManager.GetShow(slug); Show show = await _libraryManager.Get<Show>(slug);
if (show == null) if (show == null)
return NotFound(); return NotFound();
return _files.FileResult(await _thumbs.GetShowLogo(show)); return _files.FileResult(await _thumbs.GetShowLogo(show));
@ -421,7 +421,7 @@ namespace Kyoo.Api
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<IActionResult> GetBackdrop(string slug) public async Task<IActionResult> GetBackdrop(string slug)
{ {
Show show = await _libraryManager.GetShow(slug); Show show = await _libraryManager.Get<Show>(slug);
if (show == null) if (show == null)
return NotFound(); return NotFound();
return _files.FileResult(await _thumbs.GetShowBackdrop(show)); return _files.FileResult(await _thumbs.GetShowBackdrop(show));

View File

@ -35,12 +35,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.StudioID == id), ApiHelper.ParseWhere<Show>(where, x => x.StudioID == id),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetStudio(id) == null) if (!resources.Any() && await _libraryManager.Get<Studio>(id) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }
@ -61,12 +61,12 @@ namespace Kyoo.Api
{ {
try try
{ {
ICollection<Show> resources = await _libraryManager.GetShows( ICollection<Show> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Show>(where, x => x.Studio.Slug == slug), ApiHelper.ParseWhere<Show>(where, x => x.Studio.Slug == slug),
new Sort<Show>(sortBy), new Sort<Show>(sortBy),
new Pagination(limit, afterID)); new Pagination(limit, afterID));
if (!resources.Any() && await _libraryManager.GetStudio(slug) == null) if (!resources.Any() && await _libraryManager.Get<Studio>(slug) == null)
return NotFound(); return NotFound();
return Page(resources, limit); return Page(resources, limit);
} }

View File

@ -29,7 +29,7 @@ namespace Kyoo.Api
{ {
try try
{ {
return await _libraryManager.GetEpisode(x => x.Tracks.Any(y => y.ID == id)); return await _libraryManager.Get<Episode>(x => x.Tracks.Any(y => y.ID == id));
} }
catch (ItemNotFound) catch (ItemNotFound)
{ {
@ -45,7 +45,7 @@ namespace Kyoo.Api
{ {
// TODO This won't work with the local repository implementation. // TODO This won't work with the local repository implementation.
// TODO Implement something like this (a dotnet-ef's QueryCompilationContext): https://stackoverflow.com/questions/62687811/how-can-i-convert-a-custom-function-to-a-sql-expression-for-entity-framework-cor // TODO Implement something like this (a dotnet-ef's QueryCompilationContext): https://stackoverflow.com/questions/62687811/how-can-i-convert-a-custom-function-to-a-sql-expression-for-entity-framework-cor
return await _libraryManager.GetEpisode(x => x.Tracks.Any(y => y.Slug == slug)); return await _libraryManager.Get<Episode>(x => x.Tracks.Any(y => y.Slug == slug));
} }
catch (ItemNotFound) catch (ItemNotFound)
{ {

View File

@ -4,6 +4,7 @@ using Kyoo.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
@ -41,91 +42,58 @@ namespace Kyoo.Api
} }
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")] [HttpGet("{slug}")]
[HttpGet("direct/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")] [HttpGet("direct/{slug}")]
[Authorize(Policy="Play")] [Authorize(Policy="Play")]
public async Task<IActionResult> DirectEpisode(string showSlug, int seasonNumber, int episodeNumber) public async Task<IActionResult> Direct(string slug)
{ {
if (seasonNumber < 0 || episodeNumber < 0) try
return BadRequest(new {error = "Season number or episode number can not be negative."}); {
Episode episode = await _libraryManager.Get<Episode>(slug);
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode == null)
return NotFound();
return _files.FileResult(episode.Path, true); return _files.FileResult(episode.Path, true);
} }
catch (ItemNotFound)
[HttpGet("{movieSlug}")]
[HttpGet("direct/{movieSlug}")]
[Authorize(Policy="Play")]
public async Task<IActionResult> DirectMovie(string movieSlug)
{ {
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
if (episode == null)
return NotFound(); return NotFound();
return _files.FileResult(episode.Path, true); }
} }
[HttpGet("transmux/{slug}/master.m3u8")]
[HttpGet("transmux/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/master.m3u8")]
[Authorize(Policy="Play")] [Authorize(Policy="Play")]
public async Task<IActionResult> TransmuxEpisode(string showSlug, int seasonNumber, int episodeNumber) public async Task<IActionResult> Transmux(string slug)
{ {
if (seasonNumber < 0 || episodeNumber < 0) try
return BadRequest(new {error = "Season number or episode number can not be negative."}); {
Episode episode = await _libraryManager.Get<Episode>(slug);
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode == null)
return NotFound();
string path = await _transcoder.Transmux(episode); string path = await _transcoder.Transmux(episode);
if (path == null) if (path == null)
return StatusCode(500); return StatusCode(500);
return _files.FileResult(path, true); return _files.FileResult(path, true);
} }
catch (ItemNotFound)
[HttpGet("transmux/{movieSlug}/master.m3u8")]
[Authorize(Policy="Play")]
public async Task<IActionResult> TransmuxMovie(string movieSlug)
{ {
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
if (episode == null)
return NotFound(); return NotFound();
string path = await _transcoder.Transmux(episode); }
if (path == null)
return StatusCode(500);
return _files.FileResult(path, true);
} }
[HttpGet("transcode/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/master.m3u8")] [HttpGet("transcode/{slug}/master.m3u8")]
[Authorize(Policy="Play")] [Authorize(Policy="Play")]
public async Task<IActionResult> TranscodeEpisode(string showSlug, int seasonNumber, int episodeNumber) public async Task<IActionResult> Transcode(string slug)
{ {
if (seasonNumber < 0 || episodeNumber < 0) try
return BadRequest(new {error = "Season number or episode number can not be negative."}); {
Episode episode = await _libraryManager.Get<Episode>(slug);
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
if (episode == null)
return NotFound();
string path = await _transcoder.Transcode(episode); string path = await _transcoder.Transcode(episode);
if (path == null) if (path == null)
return StatusCode(500); return StatusCode(500);
return _files.FileResult(path, true); return _files.FileResult(path, true);
} }
catch (ItemNotFound)
[HttpGet("transcode/{movieSlug}/master.m3u8")]
[Authorize(Policy="Play")]
public async Task<IActionResult> TranscodeMovie(string movieSlug)
{ {
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
if (episode == null)
return NotFound(); return NotFound();
string path = await _transcoder.Transcode(episode); }
if (path == null)
return StatusCode(500);
return _files.FileResult(path, true);
} }

View File

@ -17,21 +17,11 @@ namespace Kyoo.Api
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")] [HttpGet("{slug}")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<ActionResult<WatchItem>> GetWatchItem(string showSlug, int seasonNumber, int episodeNumber) public async Task<ActionResult<WatchItem>> GetWatchItem(string slug)
{ {
Episode item = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); Episode item = await _libraryManager.Get<Episode>(slug);
if (item == null)
return NotFound();
return await WatchItem.FromEpisode(item, _libraryManager);
}
[HttpGet("{movieSlug}")]
[Authorize(Policy="Read")]
public async Task<ActionResult<WatchItem>> GetWatchItem(string movieSlug)
{
Episode item = await _libraryManager.GetMovieEpisode(movieSlug);
if (item == null) if (item == null)
return NotFound(); return NotFound();
return await WatchItem.FromEpisode(item, _libraryManager); return await WatchItem.FromEpisode(item, _libraryManager);