mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Merge pull request #25 from AnonymusRaccoon/library
Adding documentation to database's features (LibraryManager/Repositories)
This commit is contained in:
commit
0221cb7873
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -3,7 +3,7 @@
|
||||
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.
|
||||
If it fixes a bug or resolves a feature request, be sure to link to that issue.
|
||||
|
||||
## Informations
|
||||
## Information
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] New public API added
|
||||
- [ ] Non-breaking changes
|
||||
|
15
.github/workflows/analysis.yml
vendored
15
.github/workflows/analysis.yml
vendored
@ -34,11 +34,18 @@ jobs:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
dotnet test \
|
||||
'-p:CollectCoverage=true;CoverletOutputFormat=opencover' \
|
||||
'-p:SkipTranscoder=true;SkipWebApp=true' || echo "Test failed. Skipping..."
|
||||
|
||||
dotnet build-server shutdown
|
||||
|
||||
./.sonar/scanner/dotnet-sonarscanner begin \
|
||||
-k:"AnonymusRaccoon_Kyoo" \
|
||||
-o:"anonymus-raccoon" \
|
||||
-d:sonar.login="${{ secrets.SONAR_TOKEN }}" \
|
||||
-d:sonar.host.url="https://sonarcloud.io"
|
||||
-k:"AnonymusRaccoon_Kyoo" \
|
||||
-o:"anonymus-raccoon" \
|
||||
-d:sonar.login="${{ secrets.SONAR_TOKEN }}" \
|
||||
-d:sonar.host.url="https://sonarcloud.io" \
|
||||
-d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml"
|
||||
|
||||
dotnet build --no-incremental '-p:SkipTranscoder=true;SkipWebApp=true'
|
||||
|
||||
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -21,8 +21,8 @@ jobs:
|
||||
artifact: macos
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Checkout submodules
|
||||
run: git submodule update --init --recursive
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
|
@ -5,284 +5,532 @@ using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public interface ILibraryManager : IDisposable, IAsyncDisposable
|
||||
/// <summary>
|
||||
/// An interface to interract with the database. Every repository is mapped through here.
|
||||
/// </summary>
|
||||
public interface ILibraryManager
|
||||
{
|
||||
// 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; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle libraries's items (a wrapper arround shows & collections).
|
||||
/// </summary>
|
||||
ILibraryItemRepository LibraryItemRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle collections.
|
||||
/// </summary>
|
||||
ICollectionRepository CollectionRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle shows.
|
||||
/// </summary>
|
||||
IShowRepository ShowRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle seasons.
|
||||
/// </summary>
|
||||
ISeasonRepository SeasonRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle episodes.
|
||||
/// </summary>
|
||||
IEpisodeRepository EpisodeRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle tracks.
|
||||
/// </summary>
|
||||
ITrackRepository TrackRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle people.
|
||||
/// </summary>
|
||||
IPeopleRepository PeopleRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle studios.
|
||||
/// </summary>
|
||||
IStudioRepository StudioRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle genres.
|
||||
/// </summary>
|
||||
IGenreRepository GenreRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle providers.
|
||||
/// </summary>
|
||||
IProviderRepository ProviderRepository { get; }
|
||||
|
||||
// Get by id
|
||||
Task<Library> GetLibrary(int id);
|
||||
Task<Collection> GetCollection(int id);
|
||||
Task<Show> GetShow(int id);
|
||||
Task<Season> GetSeason(int id);
|
||||
Task<Season> GetSeason(int showID, int seasonNumber);
|
||||
Task<Episode> GetEpisode(int id);
|
||||
Task<Episode> GetEpisode(int showID, int seasonNumber, int episodeNumber);
|
||||
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);
|
||||
/// <summary>
|
||||
/// Get the resource by it's ID
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> Get<T>(int id) where T : class, IResource;
|
||||
|
||||
// Get by slug
|
||||
Task<Library> GetLibrary(string slug);
|
||||
Task<Collection> GetCollection(string slug);
|
||||
Task<Show> GetShow(string slug);
|
||||
Task<Season> GetSeason(string slug);
|
||||
Task<Season> GetSeason(string showSlug, int seasonNumber);
|
||||
Task<Episode> GetEpisode(string slug);
|
||||
Task<Episode> GetEpisode(string showSlug, int seasonNumber, int episodeNumber);
|
||||
Task<Episode> GetMovieEpisode(string movieSlug);
|
||||
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);
|
||||
/// <summary>
|
||||
/// Get the resource by it's slug
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> Get<T>(string slug) where T : class, IResource;
|
||||
|
||||
// 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>
|
||||
/// 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 track 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> Get(string slug, StreamType type = StreamType.Unknown);
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by it's ID or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> GetOrDefault<T>(int id) where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by it's slug or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> GetOrDefault<T>(string slug) where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by a filter function or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="where">The filter function.</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <returns>The first resource found that match the where function</returns>
|
||||
Task<T> GetOrDefault<T>(Expression<Func<T, bool>> where) where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's showID and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(int showID, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </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>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </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>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a track from it's slug and it's type or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the track</param>
|
||||
/// <param name="type">The type (Video, Audio or Subtitle)</param>
|
||||
/// <returns>The tracl found</returns>
|
||||
Task<Track> GetOrDefault(string slug, StreamType type = StreamType.Unknown);
|
||||
|
||||
|
||||
/// <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)
|
||||
where T : class, IResource
|
||||
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)
|
||||
where T : class, IResource
|
||||
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)
|
||||
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);
|
||||
|
||||
// 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,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = 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,
|
||||
[Optional] Expression<Func<LibraryItem, bool>> where,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
Pagination limit = default
|
||||
) => 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,
|
||||
Sort<LibraryItem> sort = 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,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
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,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = 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,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => 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,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = 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,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
// Show Role relations
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(int showID,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetRolesFromPeople(showID, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(string showSlug,
|
||||
/// <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,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(string showSlug,
|
||||
|
||||
/// <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,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetRolesFromPeople(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
) => GetRolesFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <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,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <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,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => 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);
|
||||
|
||||
/// <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);
|
||||
|
||||
// Get all
|
||||
Task<ICollection<Library>> GetLibraries(Expression<Func<Library, bool>> where = null,
|
||||
Sort<Library> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<Collection>> GetCollections(Expression<Func<Collection, bool>> where = null,
|
||||
Sort<Collection> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<Show>> GetShows(Expression<Func<Show, bool>> where = null,
|
||||
Sort<Show> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<Season>> GetSeasons(Expression<Func<Season, bool>> where = null,
|
||||
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,
|
||||
Expression<Func<Library, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetLibraries(where, new Sort<Library>(sort), limit);
|
||||
Task<ICollection<Collection>> GetCollections([Optional] Expression<Func<Collection, bool>> where,
|
||||
Expression<Func<Collection, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetCollections(where, new Sort<Collection>(sort), limit);
|
||||
Task<ICollection<Show>> GetShows([Optional] Expression<Func<Show, bool>> where,
|
||||
Expression<Func<Show, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetShows(where, new Sort<Show>(sort), limit);
|
||||
Task<ICollection<Track>> GetTracks([Optional] Expression<Func<Track, bool>> where,
|
||||
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 all resources with filters
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <typeparam name="T">The type of resources to load</typeparam>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default) where T : class, IResource;
|
||||
|
||||
// Counts
|
||||
Task<int> GetLibrariesCount(Expression<Func<Library, bool>> where = null);
|
||||
Task<int> GetCollectionsCount(Expression<Func<Collection, bool>> where = null);
|
||||
Task<int> GetShowsCount(Expression<Func<Show, bool>> where = null);
|
||||
Task<int> GetSeasonsCount(Expression<Func<Season, bool>> where = null);
|
||||
Task<int> GetEpisodesCount(Expression<Func<Episode, bool>> where = null);
|
||||
Task<int> GetTracksCount(Expression<Func<Track, bool>> where = null);
|
||||
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);
|
||||
/// <summary>
|
||||
/// Get all resources with filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by function</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <typeparam name="T">The type of resources to load</typeparam>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<ICollection<T>> GetAll<T>([Optional] Expression<Func<T, bool>> where,
|
||||
Expression<Func<T, object>> sort,
|
||||
Pagination limit = default) where T : class, IResource
|
||||
{
|
||||
return GetAll(where, new Sort<T>(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;
|
||||
|
||||
/// <summary>
|
||||
/// Search for a resource
|
||||
/// </summary>
|
||||
/// <param name="query">The search query</param>
|
||||
/// <typeparam name="T">The type of resources</typeparam>
|
||||
/// <returns>A list of 20 items that match the search query</returns>
|
||||
Task<ICollection<T>> Search<T>(string query) where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new resource.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to register</param>
|
||||
/// <typeparam name="T">The type of resource</typeparam>
|
||||
/// <returns>The resource registers and completed by database's informations (related items & so on)</returns>
|
||||
Task<T> Create<T>([NotNull] T item) where T : class, IResource;
|
||||
|
||||
// Search
|
||||
Task<ICollection<Library>> SearchLibraries(string searchQuery);
|
||||
Task<ICollection<Collection>> SearchCollections(string searchQuery);
|
||||
Task<ICollection<Show>> SearchShows(string searchQuery);
|
||||
Task<ICollection<Season>> SearchSeasons(string searchQuery);
|
||||
Task<ICollection<Episode>> SearchEpisodes(string searchQuery);
|
||||
Task<ICollection<Genre>> SearchGenres(string searchQuery);
|
||||
Task<ICollection<Studio>> SearchStudios(string searchQuery);
|
||||
Task<ICollection<People>> SearchPeople(string searchQuery);
|
||||
/// <summary>
|
||||
/// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to register</param>
|
||||
/// <typeparam name="T">The type of resource</typeparam>
|
||||
/// <returns>The newly created item or the existing value if it existed.</returns>
|
||||
Task<T> CreateIfNotExists<T>([NotNull] T item) where T : class, IResource;
|
||||
|
||||
//Register values
|
||||
Task<Library> RegisterLibrary(Library library);
|
||||
Task<Collection> RegisterCollection(Collection collection);
|
||||
Task<Show> RegisterShow(Show show);
|
||||
Task<Season> RegisterSeason(Season season);
|
||||
Task<Episode> RegisterEpisode(Episode episode);
|
||||
Task<Track> RegisterTrack(Track track);
|
||||
Task<Genre> RegisterGenre(Genre genre);
|
||||
Task<Studio> RegisterStudio(Studio studio);
|
||||
Task<People> RegisterPeople(People people);
|
||||
/// <summary>
|
||||
/// Edit a resource
|
||||
/// </summary>
|
||||
/// <param name="item">The resourcce to edit, it's ID can't change.</param>
|
||||
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
||||
/// <typeparam name="T">The type of resources</typeparam>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
/// <returns>The resource edited and completed by database's informations (related items & so on)</returns>
|
||||
Task<T> Edit<T>(T item, bool resetOld) where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Delete a resource.
|
||||
/// </summary>
|
||||
/// <param name="item">The resource to delete</param>
|
||||
/// <typeparam name="T">The type of resource to delete</typeparam>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task Delete<T>(T item) where T : class, IResource;
|
||||
|
||||
// Edit values
|
||||
Task<Library> EditLibrary(Library library, bool resetOld);
|
||||
Task<Collection> EditCollection(Collection collection, bool resetOld);
|
||||
Task<Show> EditShow(Show show, bool resetOld);
|
||||
Task<Season> EditSeason(Season season, bool resetOld);
|
||||
Task<Episode> EditEpisode(Episode episode, bool resetOld);
|
||||
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);
|
||||
/// <summary>
|
||||
/// Delete a resource by it's ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource to delete</param>
|
||||
/// <typeparam name="T">The type of resource to delete</typeparam>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task Delete<T>(int id) where T : class, IResource;
|
||||
|
||||
// Delete values
|
||||
Task DeleteLibrary(Library library);
|
||||
Task DeleteCollection(Collection collection);
|
||||
Task DeleteShow(Show show);
|
||||
Task DeleteSeason(Season season);
|
||||
Task DeleteEpisode(Episode episode);
|
||||
Task DeleteTrack(Track track);
|
||||
Task DeleteGenre(Genre genre);
|
||||
Task DeleteStudio(Studio studio);
|
||||
Task DeletePeople(People people);
|
||||
|
||||
//Delete by slug
|
||||
Task DeleteLibrary(string slug);
|
||||
Task DeleteCollection(string slug);
|
||||
Task DeleteShow(string slug);
|
||||
Task DeleteSeason(string slug);
|
||||
Task DeleteEpisode(string slug);
|
||||
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);
|
||||
/// <summary>
|
||||
/// Delete a resource by it's slug.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource to delete</param>
|
||||
/// <typeparam name="T">The type of resource to delete</typeparam>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task Delete<T>(string slug) where T : class, IResource;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
public interface IMetadataProvider
|
||||
{
|
||||
ProviderID Provider { get; }
|
||||
Provider Provider { get; }
|
||||
|
||||
Task<Collection> GetCollectionFromName(string name);
|
||||
|
||||
|
@ -6,28 +6,64 @@ using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Informations about the pagination. How many items should be displayed and where to start.
|
||||
/// </summary>
|
||||
public readonly struct Pagination
|
||||
{
|
||||
/// <summary>
|
||||
/// The count of items to return.
|
||||
/// </summary>
|
||||
public int Count { get; }
|
||||
/// <summary>
|
||||
/// Where to start? Using the given sort
|
||||
/// </summary>
|
||||
public int AfterID { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Pagination"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="count">Set the <see cref="Count"/> value</param>
|
||||
/// <param name="afterID">Set the <see cref="AfterID"/> value. If not specified, it will start from the start</param>
|
||||
public Pagination(int count, int afterID = 0)
|
||||
{
|
||||
Count = count;
|
||||
AfterID = afterID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly create a new pagination from a limit number.
|
||||
/// </summary>
|
||||
/// <param name="limit">Set the <see cref="Count"/> value</param>
|
||||
/// <returns>A new <see cref="Pagination"/> instance</returns>
|
||||
public static implicit operator Pagination(int limit) => new(limit);
|
||||
}
|
||||
|
||||
public struct Sort<T>
|
||||
/// <summary>
|
||||
/// Informations about how a query should be sorted. What factor should decide the sort and in which order.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">For witch type this sort applies</typeparam>
|
||||
public readonly struct Sort<T>
|
||||
{
|
||||
public Expression<Func<T, object>> Key;
|
||||
public bool Descendant;
|
||||
/// <summary>
|
||||
/// The sort key. This member will be used to sort the results.
|
||||
/// </summary>
|
||||
public Expression<Func<T, object>> Key { get; }
|
||||
/// <summary>
|
||||
/// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendent order.
|
||||
/// </summary>
|
||||
public bool Descendant { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Sort{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="key">The sort key given. It is assigned to <see cref="Key"/>.</param>
|
||||
/// <param name="descendant">Should this be in descendant order? The default is false.</param>
|
||||
/// <exception cref="ArgumentException">If the given key is not a member.</exception>
|
||||
public Sort(Expression<Func<T, object>> key, bool descendant = false)
|
||||
{
|
||||
Key = key;
|
||||
@ -37,6 +73,11 @@ namespace Kyoo.Controllers
|
||||
throw new ArgumentException("The given sort key is not valid.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Sort{T}"/> instance from a key's name (case insensitive).
|
||||
/// </summary>
|
||||
/// <param name="sortBy">A key name with an optional order specifier. Format: "key:asc", "key:desc" or "key".</param>
|
||||
/// <exception cref="ArgumentException">An invalid key or sort specifier as been given.</exception>
|
||||
public Sort(string sortBy)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sortBy))
|
||||
@ -46,8 +87,8 @@ namespace Kyoo.Controllers
|
||||
return;
|
||||
}
|
||||
|
||||
string key = sortBy.Contains(':') ? sortBy.Substring(0, sortBy.IndexOf(':')) : sortBy;
|
||||
string order = sortBy.Contains(':') ? sortBy.Substring(sortBy.IndexOf(':') + 1) : null;
|
||||
string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy;
|
||||
string order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null;
|
||||
|
||||
ParameterExpression param = Expression.Parameter(typeof(T), "x");
|
||||
MemberExpression property = Expression.Property(param, key);
|
||||
@ -64,152 +105,545 @@ namespace Kyoo.Controllers
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRepository<T> : IDisposable, IAsyncDisposable where T : class, IResource
|
||||
|
||||
/// <summary>
|
||||
/// A base class for repositories. Every service implementing this will be handled by the <see cref="LibraryManager"/>.
|
||||
/// </summary>
|
||||
public interface IBaseRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// The type for witch this repository is responsible or null if non applicable.
|
||||
/// </summary>
|
||||
Type RepositoryType { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A common repository for every resources.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The resource's type that this repository manage.</typeparam>
|
||||
public interface IRepository<T> : IBaseRepository where T : class, IResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a resource from it's ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <exception cref="ItemNotFound">If the item could not be found.</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> Get(int id);
|
||||
/// <summary>
|
||||
/// Get a resource from it's slug.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <exception cref="ItemNotFound">If the item could not be found.</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> Get(string slug);
|
||||
/// <summary>
|
||||
/// Get the first resource that match the predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter the resource.</param>
|
||||
/// <exception cref="ItemNotFound">If the item could not be found.</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> Get(Expression<Func<T, bool>> where);
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource from it's ID or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> GetOrDefault(int id);
|
||||
/// <summary>
|
||||
/// Get a resource from it's slug or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> GetOrDefault(string slug);
|
||||
/// <summary>
|
||||
/// Get the first resource that match the predicate or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter the resource.</param>
|
||||
/// <returns>The resource found</returns>
|
||||
Task<T> GetOrDefault(Expression<Func<T, bool>> where);
|
||||
|
||||
/// <summary>
|
||||
/// Search for resources.
|
||||
/// </summary>
|
||||
/// <param name="query">The query string.</param>
|
||||
/// <returns>A list of resources found</returns>
|
||||
Task<ICollection<T>> Search(string query);
|
||||
|
||||
/// <summary>
|
||||
/// Get every resources that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <param name="sort">Sort informations about the query (sort by, sort order)</param>
|
||||
/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get every resources that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <param name="sort">A sort by predicate. The order is ascending.</param>
|
||||
/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<ICollection<T>> GetAll([Optional] Expression<Func<T, bool>> where,
|
||||
Expression<Func<T, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetAll(where, new Sort<T>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of resources that match the filter's predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <returns>How many resources matched that filter</returns>
|
||||
Task<int> GetCount(Expression<Func<T, bool>> where = null);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a new resource.
|
||||
/// </summary>
|
||||
/// <param name="obj">The item to register</param>
|
||||
/// <returns>The resource registers and completed by database's informations (related items & so on)</returns>
|
||||
Task<T> Create([NotNull] T obj);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to create</param>
|
||||
/// <param name="silentFail">Allow issues to occurs in this method. Every issue is catched and ignored.</param>
|
||||
/// <returns>The newly created item or the existing value if it existed.</returns>
|
||||
Task<T> CreateIfNotExists([NotNull] T obj, bool silentFail = false);
|
||||
|
||||
/// <summary>
|
||||
/// Edit a resource
|
||||
/// </summary>
|
||||
/// <param name="edited">The resourcce to edit, it's ID can't change.</param>
|
||||
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
/// <returns>The resource edited and completed by database's informations (related items & so on)</returns>
|
||||
Task<T> Edit([NotNull] T edited, bool resetOld);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a resource by it's ID
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the resource</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task Delete(int id);
|
||||
/// <summary>
|
||||
/// Delete a resource by it's slug
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task Delete(string slug);
|
||||
/// <summary>
|
||||
/// Delete a resource
|
||||
/// </summary>
|
||||
/// <param name="obj">The resource to delete</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task Delete([NotNull] T obj);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a list of resources.
|
||||
/// </summary>
|
||||
/// <param name="objs">One or multiple resources to delete</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task DeleteRange(params T[] objs) => DeleteRange(objs.AsEnumerable());
|
||||
/// <summary>
|
||||
/// Delete a list of resources.
|
||||
/// </summary>
|
||||
/// <param name="objs">An enumerable of resources to delete</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task DeleteRange(IEnumerable<T> objs);
|
||||
/// <summary>
|
||||
/// Delete a list of resources.
|
||||
/// </summary>
|
||||
/// <param name="ids">One or multiple resources's id</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task DeleteRange(params int[] ids) => DeleteRange(ids.AsEnumerable());
|
||||
/// <summary>
|
||||
/// Delete a list of resources.
|
||||
/// </summary>
|
||||
/// <param name="ids">An enumearble of resources's id</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task DeleteRange(IEnumerable<int> ids);
|
||||
/// <summary>
|
||||
/// Delete a list of resources.
|
||||
/// </summary>
|
||||
/// <param name="slugs">One or multiple resources's slug</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task DeleteRange(params string[] slugs) => DeleteRange(slugs.AsEnumerable());
|
||||
/// <summary>
|
||||
/// Delete a list of resources.
|
||||
/// </summary>
|
||||
/// <param name="slugs">An enumerable of resources's slug</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task DeleteRange(IEnumerable<string> slugs);
|
||||
/// <summary>
|
||||
/// Delete a list of resources.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
Task DeleteRange([NotNull] Expression<Func<T, bool>> where);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle shows.
|
||||
/// </summary>
|
||||
public interface IShowRepository : IRepository<Show>
|
||||
{
|
||||
/// <summary>
|
||||
/// Link a show to a collection and/or a library. The given show is now part of thoses containers.
|
||||
/// If both a library and a collection are given, the collection is added to the library too.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <param name="libraryID">The ID of the library (optional)</param>
|
||||
/// <param name="collectionID">The ID of the collection (optional)</param>
|
||||
Task AddShowLink(int showID, int? libraryID, int? collectionID);
|
||||
|
||||
/// <summary>
|
||||
/// Get a show's slug from it's ID.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <exception cref="ItemNotFound">If a show with the given ID is not found.</exception>
|
||||
/// <returns>The show's slug</returns>
|
||||
Task<string> GetSlug(int showID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle seasons.
|
||||
/// </summary>
|
||||
public interface ISeasonRepository : IRepository<Season>
|
||||
{
|
||||
/// <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);
|
||||
Task Delete(string showSlug, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's showID and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(int showID, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The repository to handle episodes
|
||||
/// </summary>
|
||||
public interface IEpisodeRepository : IRepository<Episode>
|
||||
{
|
||||
/// <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);
|
||||
Task<Episode> Get(int seasonID, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </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>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber);
|
||||
/// <summary>
|
||||
/// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </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>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID and it's absolute number.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="absoluteNumber">The episode's absolute number (The episode number does not reset to 1 after the end of a season.</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetAbsolute(int showID, int absoluteNumber);
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID and it's absolute number.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="absoluteNumber">The episode's absolute number (The episode number does not reset to 1 after the end of a season.</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetAbsolute(string showSlug, int absoluteNumber);
|
||||
Task Delete(string showSlug, int seasonNumber, int episodeNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle tracks
|
||||
/// </summary>
|
||||
public interface ITrackRepository : IRepository<Track>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a track 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> Get(string slug, StreamType type = StreamType.Unknown);
|
||||
|
||||
/// <summary>
|
||||
/// Get a track from it's slug and it's type or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the track</param>
|
||||
/// <param name="type">The type (Video, Audio or Subtitle)</param>
|
||||
/// <returns>The tracl found</returns>
|
||||
Task<Track> GetOrDefault(string slug, StreamType type = StreamType.Unknown);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle libraries.
|
||||
/// </summary>
|
||||
public interface ILibraryRepository : IRepository<Library> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle library items (A wrapper arround shows and collections).
|
||||
/// </summary>
|
||||
public interface ILibraryItemRepository : IRepository<LibraryItem>
|
||||
{
|
||||
/// <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>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = 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>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||
[Optional] Expression<Func<LibraryItem, bool>> where,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
|
||||
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(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>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(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>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
||||
[Optional] Expression<Func<LibraryItem, bool>> where,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromLibrary(librarySlug, where, new Sort<LibraryItem>(sort), limit);
|
||||
) => GetFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository for collections
|
||||
/// </summary>
|
||||
public interface ICollectionRepository : IRepository<Collection> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository for genres.
|
||||
/// </summary>
|
||||
public interface IGenreRepository : IRepository<Genre> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository for studios.
|
||||
/// </summary>
|
||||
public interface IStudioRepository : IRepository<Studio> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository for people.
|
||||
/// </summary>
|
||||
public interface IPeopleRepository : IRepository<People>
|
||||
{
|
||||
/// <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>> GetFromShow(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = 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>> GetFromShow(int showID,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromShow(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>> GetFromShow(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = 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>> GetFromShow(string showSlug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(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>> GetFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(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>> GetFromPeople(int id,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromPeople(showID, where, new Sort<PeopleRole>(sort), limit);
|
||||
) => GetFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(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>> GetFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(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>> GetFromPeople(string slug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromPeople(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
) => GetFromPeople(slug, where, new Sort<PeopleRole>(sort), limit);
|
||||
}
|
||||
|
||||
public interface IProviderRepository : IRepository<ProviderID>
|
||||
/// <summary>
|
||||
/// A repository to handle providers.
|
||||
/// </summary>
|
||||
public interface IProviderRepository : IRepository<Provider>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a list of external ids that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to add arbitrary filter</param>
|
||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
||||
/// <param name="limit">Paginations information (where to start and how many to get)</param>
|
||||
/// <returns>A filtered list of external ids.</returns>
|
||||
Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null,
|
||||
Sort<MetadataID> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of external ids that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to add arbitrary filter</param>
|
||||
/// <param name="sort">A sort by expression</param>
|
||||
/// <param name="limit">Paginations information (where to start and how many to get)</param>
|
||||
/// <returns>A filtered list of external ids.</returns>
|
||||
Task<ICollection<MetadataID>> GetMetadataID([Optional] Expression<Func<MetadataID, bool>> where,
|
||||
Expression<Func<MetadataID, object>> sort,
|
||||
Pagination limit = default
|
||||
|
@ -11,7 +11,7 @@ namespace Kyoo.Controllers
|
||||
Task Validate(Season season, bool alwaysDownload = false);
|
||||
Task Validate(Episode episode, 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> GetShowLogo([NotNull] Show show);
|
||||
@ -19,6 +19,6 @@ namespace Kyoo.Controllers
|
||||
Task<string> GetSeasonPoster([NotNull] Season season);
|
||||
Task<string> GetEpisodeThumb([NotNull] Episode episode);
|
||||
Task<string> GetPeoplePoster([NotNull] People people);
|
||||
Task<string> GetProviderLogo([NotNull] ProviderID provider);
|
||||
Task<string> GetProviderLogo([NotNull] Provider provider);
|
||||
}
|
||||
}
|
||||
|
@ -4,248 +4,174 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class LibraryManager : ILibraryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of repositories
|
||||
/// </summary>
|
||||
private readonly IBaseRepository[] _repositories;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ILibraryRepository LibraryRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public ILibraryItemRepository LibraryItemRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public ICollectionRepository CollectionRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public IShowRepository ShowRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public ISeasonRepository SeasonRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public IEpisodeRepository EpisodeRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public ITrackRepository TrackRepository { get; }
|
||||
public IGenreRepository GenreRepository { get; }
|
||||
public IStudioRepository StudioRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public IPeopleRepository PeopleRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public IStudioRepository StudioRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public IGenreRepository GenreRepository { get; }
|
||||
/// <inheritdoc />
|
||||
public IProviderRepository ProviderRepository { get; }
|
||||
|
||||
public LibraryManager(ILibraryRepository libraryRepository,
|
||||
ILibraryItemRepository libraryItemRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IShowRepository showRepository,
|
||||
ISeasonRepository seasonRepository,
|
||||
IEpisodeRepository episodeRepository,
|
||||
ITrackRepository trackRepository,
|
||||
IGenreRepository genreRepository,
|
||||
IStudioRepository studioRepository,
|
||||
IProviderRepository providerRepository,
|
||||
IPeopleRepository peopleRepository)
|
||||
{
|
||||
LibraryRepository = libraryRepository;
|
||||
LibraryItemRepository = libraryItemRepository;
|
||||
CollectionRepository = collectionRepository;
|
||||
ShowRepository = showRepository;
|
||||
SeasonRepository = seasonRepository;
|
||||
EpisodeRepository = episodeRepository;
|
||||
TrackRepository = trackRepository;
|
||||
GenreRepository = genreRepository;
|
||||
StudioRepository = studioRepository;
|
||||
ProviderRepository = providerRepository;
|
||||
PeopleRepository = peopleRepository;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LibraryRepository.Dispose();
|
||||
CollectionRepository.Dispose();
|
||||
ShowRepository.Dispose();
|
||||
SeasonRepository.Dispose();
|
||||
EpisodeRepository.Dispose();
|
||||
TrackRepository.Dispose();
|
||||
GenreRepository.Dispose();
|
||||
StudioRepository.Dispose();
|
||||
PeopleRepository.Dispose();
|
||||
ProviderRepository.Dispose();
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
/// <summary>
|
||||
/// Create a new <see cref="LibraryManager"/> instancce with every repository available.
|
||||
/// </summary>
|
||||
/// <param name="repositories">The list of repositories that this library manager should manage.
|
||||
/// If a repository for every base type is not available, this instance won't be stable.</param>
|
||||
public LibraryManager(IEnumerable<IBaseRepository> repositories)
|
||||
{
|
||||
await Task.WhenAll(
|
||||
LibraryRepository.DisposeAsync().AsTask(),
|
||||
CollectionRepository.DisposeAsync().AsTask(),
|
||||
ShowRepository.DisposeAsync().AsTask(),
|
||||
SeasonRepository.DisposeAsync().AsTask(),
|
||||
EpisodeRepository.DisposeAsync().AsTask(),
|
||||
TrackRepository.DisposeAsync().AsTask(),
|
||||
GenreRepository.DisposeAsync().AsTask(),
|
||||
StudioRepository.DisposeAsync().AsTask(),
|
||||
PeopleRepository.DisposeAsync().AsTask(),
|
||||
ProviderRepository.DisposeAsync().AsTask()
|
||||
);
|
||||
_repositories = repositories.ToArray();
|
||||
LibraryRepository = GetRepository<Library>() as ILibraryRepository;
|
||||
LibraryItemRepository = GetRepository<LibraryItem>() as ILibraryItemRepository;
|
||||
CollectionRepository = GetRepository<Collection>() as ICollectionRepository;
|
||||
ShowRepository = GetRepository<Show>() as IShowRepository;
|
||||
SeasonRepository = GetRepository<Season>() as ISeasonRepository;
|
||||
EpisodeRepository = GetRepository<Episode>() as IEpisodeRepository;
|
||||
TrackRepository = GetRepository<Track>() as ITrackRepository;
|
||||
PeopleRepository = GetRepository<People>() as IPeopleRepository;
|
||||
StudioRepository = GetRepository<Studio>() as IStudioRepository;
|
||||
GenreRepository = GetRepository<Genre>() as IGenreRepository;
|
||||
ProviderRepository = GetRepository<Provider>() as IProviderRepository;
|
||||
}
|
||||
|
||||
public Task<Library> GetLibrary(int id)
|
||||
/// <inheritdoc />
|
||||
public IRepository<T> GetRepository<T>()
|
||||
where T : class, IResource
|
||||
{
|
||||
return LibraryRepository.Get(id);
|
||||
if (_repositories.FirstOrDefault(x => x.RepositoryType == typeof(T)) is IRepository<T> ret)
|
||||
return ret;
|
||||
throw new ItemNotFound();
|
||||
}
|
||||
|
||||
public Task<Collection> GetCollection(int id)
|
||||
/// <inheritdoc />
|
||||
public Task<T> Get<T>(int id)
|
||||
where T : class, IResource
|
||||
{
|
||||
return CollectionRepository.Get(id);
|
||||
return GetRepository<T>().Get(id);
|
||||
}
|
||||
|
||||
public Task<Show> GetShow(int id)
|
||||
/// <inheritdoc />
|
||||
public Task<T> Get<T>(string slug)
|
||||
where T : class, IResource
|
||||
{
|
||||
return ShowRepository.Get(id);
|
||||
return GetRepository<T>().Get(slug);
|
||||
}
|
||||
|
||||
public Task<Season> GetSeason(int id)
|
||||
/// <inheritdoc />
|
||||
public Task<T> Get<T>(Expression<Func<T, bool>> where)
|
||||
where T : class, IResource
|
||||
{
|
||||
return SeasonRepository.Get(id);
|
||||
return GetRepository<T>().Get(where);
|
||||
}
|
||||
|
||||
public Task<Season> GetSeason(int showID, int seasonNumber)
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Season> Get(int showID, int seasonNumber)
|
||||
{
|
||||
return SeasonRepository.Get(showID, seasonNumber);
|
||||
}
|
||||
|
||||
public Task<Episode> GetEpisode(int id)
|
||||
{
|
||||
return EpisodeRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<Episode> GetEpisode(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return EpisodeRepository.Get(showID, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Task<Track> GetTrack(string slug, StreamType type = StreamType.Unknown)
|
||||
{
|
||||
return TrackRepository.Get(slug, type);
|
||||
}
|
||||
|
||||
public Task<Genre> GetGenre(int id)
|
||||
{
|
||||
return GenreRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<Studio> GetStudio(int id)
|
||||
{
|
||||
return StudioRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<People> GetPeople(int id)
|
||||
{
|
||||
return PeopleRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<ProviderID> GetProvider(int id)
|
||||
{
|
||||
return ProviderRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<Library> GetLibrary(string slug)
|
||||
{
|
||||
return LibraryRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<Collection> GetCollection(string slug)
|
||||
{
|
||||
return CollectionRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<Show> GetShow(string slug)
|
||||
{
|
||||
return ShowRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<Season> GetSeason(string slug)
|
||||
{
|
||||
return SeasonRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<Season> GetSeason(string showSlug, int seasonNumber)
|
||||
/// <inheritdoc />
|
||||
public Task<Season> Get(string showSlug, int seasonNumber)
|
||||
{
|
||||
return SeasonRepository.Get(showSlug, seasonNumber);
|
||||
}
|
||||
|
||||
public Task<Episode> GetEpisode(string slug)
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> Get(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return EpisodeRepository.Get(slug);
|
||||
return EpisodeRepository.Get(showID, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Task<Episode> GetEpisode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return EpisodeRepository.Get(showSlug, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Task<Episode> GetMovieEpisode(string movieSlug)
|
||||
/// <inheritdoc />
|
||||
public Task<Track> Get(string slug, StreamType type = StreamType.Unknown)
|
||||
{
|
||||
return EpisodeRepository.Get(movieSlug);
|
||||
return TrackRepository.Get(slug, type);
|
||||
}
|
||||
|
||||
public Task<Track> GetTrack(int id)
|
||||
/// <inheritdoc />
|
||||
public async Task<T> GetOrDefault<T>(int id)
|
||||
where T : class, IResource
|
||||
{
|
||||
return TrackRepository.Get(id);
|
||||
}
|
||||
|
||||
public Task<Genre> GetGenre(string slug)
|
||||
{
|
||||
return GenreRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<Studio> GetStudio(string slug)
|
||||
{
|
||||
return StudioRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<People> GetPeople(string slug)
|
||||
{
|
||||
return PeopleRepository.Get(slug);
|
||||
return await GetRepository<T>().GetOrDefault(id);
|
||||
}
|
||||
|
||||
public Task<ProviderID> GetProvider(string slug)
|
||||
/// <inheritdoc />
|
||||
public async Task<T> GetOrDefault<T>(string slug)
|
||||
where T : class, IResource
|
||||
{
|
||||
return ProviderRepository.Get(slug);
|
||||
return await GetRepository<T>().GetOrDefault(slug);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<T> GetOrDefault<T>(Expression<Func<T, bool>> where)
|
||||
where T : class, IResource
|
||||
{
|
||||
return await GetRepository<T>().GetOrDefault(where);
|
||||
}
|
||||
|
||||
public Task<Library> GetLibrary(Expression<Func<Library, bool>> where)
|
||||
/// <inheritdoc />
|
||||
public async Task<Season> GetOrDefault(int showID, int seasonNumber)
|
||||
{
|
||||
return LibraryRepository.Get(where);
|
||||
return await SeasonRepository.GetOrDefault(showID, seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Season> GetOrDefault(string showSlug, int seasonNumber)
|
||||
{
|
||||
return await SeasonRepository.GetOrDefault(showSlug, seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return await EpisodeRepository.GetOrDefault(showID, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return await EpisodeRepository.GetOrDefault(showSlug, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Task<Collection> GetCollection(Expression<Func<Collection, bool>> where)
|
||||
/// <inheritdoc />
|
||||
public async Task<Track> GetOrDefault(string slug, StreamType type = StreamType.Unknown)
|
||||
{
|
||||
return CollectionRepository.Get(where);
|
||||
return await TrackRepository.GetOrDefault(slug, type);
|
||||
}
|
||||
|
||||
public Task<Show> GetShow(Expression<Func<Show, bool>> where)
|
||||
{
|
||||
return ShowRepository.Get(where);
|
||||
}
|
||||
|
||||
public Task<Season> GetSeason(Expression<Func<Season, bool>> where)
|
||||
{
|
||||
return SeasonRepository.Get(where);
|
||||
}
|
||||
|
||||
public Task<Episode> GetEpisode(Expression<Func<Episode, bool>> where)
|
||||
{
|
||||
return EpisodeRepository.Get(where);
|
||||
}
|
||||
|
||||
public Task<Track> GetTrack(Expression<Func<Track, bool>> where)
|
||||
{
|
||||
return TrackRepository.Get(where);
|
||||
}
|
||||
|
||||
public Task<Genre> GetGenre(Expression<Func<Genre, bool>> where)
|
||||
{
|
||||
return GenreRepository.Get(where);
|
||||
}
|
||||
|
||||
public Task<Studio> GetStudio(Expression<Func<Studio, bool>> where)
|
||||
{
|
||||
return StudioRepository.Get(where);
|
||||
}
|
||||
|
||||
public Task<People> GetPerson(Expression<Func<People, bool>> where)
|
||||
{
|
||||
return PeopleRepository.Get(where);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<T> Load<T, T2>(T obj, Expression<Func<T, T2>> member)
|
||||
where T : class, IResource
|
||||
where T2 : class, IResource, new()
|
||||
@ -255,6 +181,7 @@ namespace Kyoo.Controllers
|
||||
return Load(obj, Utility.GetPropertyName(member));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<T> Load<T, T2>(T obj, Expression<Func<T, ICollection<T2>>> member)
|
||||
where T : class, IResource
|
||||
where T2 : class, new()
|
||||
@ -264,14 +191,24 @@ namespace Kyoo.Controllers
|
||||
return Load(obj, Utility.GetPropertyName(member));
|
||||
}
|
||||
|
||||
public async Task<T> Load<T>(T obj, string member)
|
||||
/// <inheritdoc />
|
||||
public async Task<T> Load<T>(T obj, string memberName)
|
||||
where T : class, IResource
|
||||
{
|
||||
await Load(obj as IResource, member);
|
||||
await Load(obj as IResource, memberName);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private async Task SetRelation<T1, T2>(T1 obj,
|
||||
/// <summary>
|
||||
/// Set relations between to objects.
|
||||
/// </summary>
|
||||
/// <param name="obj">The owner object</param>
|
||||
/// <param name="loader">A Task to load a collection of related objects</param>
|
||||
/// <param name="setter">A setter function to store the collection of related objects</param>
|
||||
/// <param name="inverse">A setter function to store the owner of a releated object loaded</param>
|
||||
/// <typeparam name="T1">The type of the owner object</typeparam>
|
||||
/// <typeparam name="T2">The type of the related object</typeparam>
|
||||
private static async Task SetRelation<T1, T2>(T1 obj,
|
||||
Task<ICollection<T2>> loader,
|
||||
Action<T1, ICollection<T2>> setter,
|
||||
Action<T2, T1> inverse)
|
||||
@ -282,12 +219,13 @@ namespace Kyoo.Controllers
|
||||
inverse(item, obj);
|
||||
}
|
||||
|
||||
public Task Load(IResource obj, string member)
|
||||
/// <inheritdoc />
|
||||
public Task Load(IResource obj, string memberName)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
return (obj, member) switch
|
||||
return (obj, member: memberName) switch
|
||||
{
|
||||
(Library l, nameof(Library.Providers)) => ProviderRepository
|
||||
.GetAll(x => x.Libraries.Any(y => y.ID == obj.ID))
|
||||
@ -343,7 +281,7 @@ namespace Kyoo.Controllers
|
||||
.Then(x => s.Collections = x),
|
||||
|
||||
(Show s, nameof(Show.Studio)) => StudioRepository
|
||||
.Get(x => x.Shows.Any(y => y.ID == obj.ID))
|
||||
.GetOrDefault(x => x.Shows.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
s.Studio = x;
|
||||
@ -362,7 +300,7 @@ namespace Kyoo.Controllers
|
||||
(x, y) => { x.Season = y; x.SeasonID = y.ID; }),
|
||||
|
||||
(Season s, nameof(Season.Show)) => ShowRepository
|
||||
.Get(x => x.Seasons.Any(y => y.ID == obj.ID))
|
||||
.GetOrDefault(x => x.Seasons.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
s.Show = x;
|
||||
@ -381,7 +319,7 @@ namespace Kyoo.Controllers
|
||||
(x, y) => { x.Episode = y; x.EpisodeID = y.ID; }),
|
||||
|
||||
(Episode e, nameof(Episode.Show)) => ShowRepository
|
||||
.Get(x => x.Episodes.Any(y => y.ID == obj.ID))
|
||||
.GetOrDefault(x => x.Episodes.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
e.Show = x;
|
||||
@ -389,7 +327,7 @@ namespace Kyoo.Controllers
|
||||
}),
|
||||
|
||||
(Episode e, nameof(Episode.Season)) => SeasonRepository
|
||||
.Get(x => x.Episodes.Any(y => y.ID == e.ID))
|
||||
.GetOrDefault(x => x.Episodes.Any(y => y.ID == e.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
e.Season = x;
|
||||
@ -398,7 +336,7 @@ namespace Kyoo.Controllers
|
||||
|
||||
|
||||
(Track t, nameof(Track.Episode)) => EpisodeRepository
|
||||
.Get(x => x.Tracks.Any(y => y.ID == obj.ID))
|
||||
.GetOrDefault(x => x.Tracks.Any(y => y.ID == obj.ID))
|
||||
.Then(x =>
|
||||
{
|
||||
t.Episode = x;
|
||||
@ -426,85 +364,16 @@ namespace Kyoo.Controllers
|
||||
.Then(x => p.Roles = x),
|
||||
|
||||
|
||||
(ProviderID p, nameof(ProviderID.Libraries)) => LibraryRepository
|
||||
(Provider p, nameof(Provider.Libraries)) => LibraryRepository
|
||||
.GetAll(x => x.Providers.Any(y => y.ID == obj.ID))
|
||||
.Then(x => p.Libraries = x),
|
||||
|
||||
|
||||
_ => throw new ArgumentException($"Couldn't find a way to load {member} of {obj.Slug}.")
|
||||
_ => throw new ArgumentException($"Couldn't find a way to load {memberName} of {obj.Slug}.")
|
||||
};
|
||||
}
|
||||
|
||||
public Task<ICollection<Library>> GetLibraries(Expression<Func<Library, bool>> where = null,
|
||||
Sort<Library> sort = default,
|
||||
Pagination page = default)
|
||||
{
|
||||
return LibraryRepository.GetAll(where, sort, page);
|
||||
}
|
||||
|
||||
public Task<ICollection<Collection>> GetCollections(Expression<Func<Collection, bool>> where = null,
|
||||
Sort<Collection> sort = default,
|
||||
Pagination page = default)
|
||||
{
|
||||
return CollectionRepository.GetAll(where, sort, page);
|
||||
}
|
||||
|
||||
public Task<ICollection<Show>> GetShows(Expression<Func<Show, bool>> where = null,
|
||||
Sort<Show> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return ShowRepository.GetAll(where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<Season>> GetSeasons(Expression<Func<Season, bool>> where = null,
|
||||
Sort<Season> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return SeasonRepository.GetAll(where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<Episode>> GetEpisodes(Expression<Func<Episode, bool>> where = null,
|
||||
Sort<Episode> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return EpisodeRepository.GetAll(where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<Track>> GetTracks(Expression<Func<Track, bool>> where = null,
|
||||
Sort<Track> sort = default,
|
||||
Pagination page = default)
|
||||
{
|
||||
return TrackRepository.GetAll(where, sort, page);
|
||||
}
|
||||
|
||||
public Task<ICollection<Studio>> GetStudios(Expression<Func<Studio, bool>> where = null,
|
||||
Sort<Studio> sort = default,
|
||||
Pagination page = default)
|
||||
{
|
||||
return StudioRepository.GetAll(where, sort, page);
|
||||
}
|
||||
|
||||
public Task<ICollection<People>> GetPeople(Expression<Func<People, bool>> where = null,
|
||||
Sort<People> sort = default,
|
||||
Pagination page = default)
|
||||
{
|
||||
return PeopleRepository.GetAll(where, sort, page);
|
||||
}
|
||||
|
||||
public Task<ICollection<Genre>> GetGenres(Expression<Func<Genre, bool>> where = null,
|
||||
Sort<Genre> sort = default,
|
||||
Pagination page = default)
|
||||
{
|
||||
return GenreRepository.GetAll(where, sort, page);
|
||||
}
|
||||
|
||||
public Task<ICollection<ProviderID>> GetProviders(Expression<Func<ProviderID, bool>> where = null,
|
||||
Sort<ProviderID> sort = default,
|
||||
Pagination page = default)
|
||||
{
|
||||
return ProviderRepository.GetAll(where, sort, page);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
@ -513,366 +382,128 @@ namespace Kyoo.Controllers
|
||||
return LibraryItemRepository.GetFromLibrary(id, where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<LibraryItem>> GetItemsFromLibrary(string librarySlug,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<LibraryItem>> GetItemsFromLibrary(string slug,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return LibraryItemRepository.GetFromLibrary(librarySlug, where, sort, limit);
|
||||
return LibraryItemRepository.GetFromLibrary(slug, where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromShow(showID, where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromShow(showSlug, where, sort, limit);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromPeople(id, where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromPeople(slug, where, sort, limit);
|
||||
}
|
||||
|
||||
public Task<int> GetLibrariesCount(Expression<Func<Library, bool>> where = null)
|
||||
{
|
||||
return LibraryRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetCollectionsCount(Expression<Func<Collection, bool>> where = null)
|
||||
{
|
||||
return CollectionRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetShowsCount(Expression<Func<Show, bool>> where = null)
|
||||
{
|
||||
return ShowRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetSeasonsCount(Expression<Func<Season, bool>> where = null)
|
||||
{
|
||||
return SeasonRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetEpisodesCount(Expression<Func<Episode, bool>> where = null)
|
||||
{
|
||||
return EpisodeRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetTracksCount(Expression<Func<Track, bool>> where = null)
|
||||
{
|
||||
return TrackRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetGenresCount(Expression<Func<Genre, bool>> where = null)
|
||||
{
|
||||
return GenreRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetStudiosCount(Expression<Func<Studio, bool>> where = null)
|
||||
{
|
||||
return StudioRepository.GetCount(where);
|
||||
}
|
||||
|
||||
public Task<int> GetPeopleCount(Expression<Func<People, bool>> where = null)
|
||||
{
|
||||
return PeopleRepository.GetCount(where);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddShowLink(int showID, int? libraryID, int? collectionID)
|
||||
{
|
||||
return ShowRepository.AddShowLink(showID, libraryID, collectionID);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddShowLink(Show show, Library library, Collection collection)
|
||||
{
|
||||
if (show == null)
|
||||
throw new ArgumentNullException(nameof(show));
|
||||
return AddShowLink(show.ID, library?.ID, collection?.ID);
|
||||
return ShowRepository.AddShowLink(show.ID, library?.ID, collection?.ID);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
where T : class, IResource
|
||||
{
|
||||
return GetRepository<T>().GetAll(where, sort, limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> GetCount<T>(Expression<Func<T, bool>> where = null)
|
||||
where T : class, IResource
|
||||
{
|
||||
return GetRepository<T>().GetCount(where);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<T>> Search<T>(string query)
|
||||
where T : class, IResource
|
||||
{
|
||||
return GetRepository<T>().Search(query);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<T> Create<T>(T item)
|
||||
where T : class, IResource
|
||||
{
|
||||
return GetRepository<T>().Create(item);
|
||||
}
|
||||
|
||||
public Task<ICollection<Library>> SearchLibraries(string searchQuery)
|
||||
/// <inheritdoc />
|
||||
public Task<T> CreateIfNotExists<T>(T item)
|
||||
where T : class, IResource
|
||||
{
|
||||
return LibraryRepository.Search(searchQuery);
|
||||
return GetRepository<T>().CreateIfNotExists(item);
|
||||
}
|
||||
|
||||
public Task<ICollection<Collection>> SearchCollections(string searchQuery)
|
||||
/// <inheritdoc />
|
||||
public Task<T> Edit<T>(T item, bool resetOld)
|
||||
where T : class, IResource
|
||||
{
|
||||
return CollectionRepository.Search(searchQuery);
|
||||
return GetRepository<T>().Edit(item, resetOld);
|
||||
}
|
||||
|
||||
public Task<ICollection<Show>> SearchShows(string searchQuery)
|
||||
/// <inheritdoc />
|
||||
public Task Delete<T>(T item)
|
||||
where T : class, IResource
|
||||
{
|
||||
return ShowRepository.Search(searchQuery);
|
||||
return GetRepository<T>().Delete(item);
|
||||
}
|
||||
|
||||
public Task<ICollection<Season>> SearchSeasons(string searchQuery)
|
||||
/// <inheritdoc />
|
||||
public Task Delete<T>(int id)
|
||||
where T : class, IResource
|
||||
{
|
||||
return SeasonRepository.Search(searchQuery);
|
||||
return GetRepository<T>().Delete(id);
|
||||
}
|
||||
|
||||
public Task<ICollection<Episode>> SearchEpisodes(string searchQuery)
|
||||
/// <inheritdoc />
|
||||
public Task Delete<T>(string slug)
|
||||
where T : class, IResource
|
||||
{
|
||||
return EpisodeRepository.Search(searchQuery);
|
||||
}
|
||||
|
||||
public Task<ICollection<Genre>> SearchGenres(string searchQuery)
|
||||
{
|
||||
return GenreRepository.Search(searchQuery);
|
||||
}
|
||||
|
||||
public Task<ICollection<Studio>> SearchStudios(string searchQuery)
|
||||
{
|
||||
return StudioRepository.Search(searchQuery);
|
||||
}
|
||||
|
||||
public Task<ICollection<People>> SearchPeople(string searchQuery)
|
||||
{
|
||||
return PeopleRepository.Search(searchQuery);
|
||||
}
|
||||
|
||||
public Task<Library> RegisterLibrary(Library library)
|
||||
{
|
||||
return LibraryRepository.Create(library);
|
||||
}
|
||||
|
||||
public Task<Collection> RegisterCollection(Collection collection)
|
||||
{
|
||||
return CollectionRepository.Create(collection);
|
||||
}
|
||||
|
||||
public Task<Show> RegisterShow(Show show)
|
||||
{
|
||||
return ShowRepository.Create(show);
|
||||
}
|
||||
|
||||
public Task<Season> RegisterSeason(Season season)
|
||||
{
|
||||
return SeasonRepository.Create(season);
|
||||
}
|
||||
|
||||
public Task<Episode> RegisterEpisode(Episode episode)
|
||||
{
|
||||
return EpisodeRepository.Create(episode);
|
||||
}
|
||||
|
||||
public Task<Track> RegisterTrack(Track track)
|
||||
{
|
||||
return TrackRepository.Create(track);
|
||||
}
|
||||
|
||||
public Task<Genre> RegisterGenre(Genre genre)
|
||||
{
|
||||
return GenreRepository.Create(genre);
|
||||
}
|
||||
|
||||
public Task<Studio> RegisterStudio(Studio studio)
|
||||
{
|
||||
return StudioRepository.Create(studio);
|
||||
}
|
||||
|
||||
public Task<People> RegisterPeople(People people)
|
||||
{
|
||||
return PeopleRepository.Create(people);
|
||||
}
|
||||
|
||||
public Task<Library> EditLibrary(Library library, bool resetOld)
|
||||
{
|
||||
return LibraryRepository.Edit(library, resetOld);
|
||||
}
|
||||
|
||||
public Task<Collection> EditCollection(Collection collection, bool resetOld)
|
||||
{
|
||||
return CollectionRepository.Edit(collection, resetOld);
|
||||
}
|
||||
|
||||
public Task<Show> EditShow(Show show, bool resetOld)
|
||||
{
|
||||
return ShowRepository.Edit(show, resetOld);
|
||||
}
|
||||
|
||||
public Task<Season> EditSeason(Season season, bool resetOld)
|
||||
{
|
||||
return SeasonRepository.Edit(season, resetOld);
|
||||
}
|
||||
|
||||
public Task<Episode> EditEpisode(Episode episode, bool resetOld)
|
||||
{
|
||||
return EpisodeRepository.Edit(episode, resetOld);
|
||||
}
|
||||
|
||||
public Task<Track> EditTrack(Track track, bool resetOld)
|
||||
{
|
||||
return TrackRepository.Edit(track, resetOld);
|
||||
}
|
||||
|
||||
public Task<Genre> EditGenre(Genre genre, bool resetOld)
|
||||
{
|
||||
return GenreRepository.Edit(genre, resetOld);
|
||||
}
|
||||
|
||||
public Task<Studio> EditStudio(Studio studio, bool resetOld)
|
||||
{
|
||||
return StudioRepository.Edit(studio, resetOld);
|
||||
}
|
||||
|
||||
public Task<People> EditPeople(People people, bool resetOld)
|
||||
{
|
||||
return PeopleRepository.Edit(people, resetOld);
|
||||
}
|
||||
|
||||
public Task DeleteLibrary(Library library)
|
||||
{
|
||||
return LibraryRepository.Delete(library);
|
||||
}
|
||||
|
||||
public Task DeleteCollection(Collection collection)
|
||||
{
|
||||
return CollectionRepository.Delete(collection);
|
||||
}
|
||||
|
||||
public Task DeleteShow(Show show)
|
||||
{
|
||||
return ShowRepository.Delete(show);
|
||||
}
|
||||
|
||||
public Task DeleteSeason(Season season)
|
||||
{
|
||||
return SeasonRepository.Delete(season);
|
||||
}
|
||||
|
||||
public Task DeleteEpisode(Episode episode)
|
||||
{
|
||||
return EpisodeRepository.Delete(episode);
|
||||
}
|
||||
|
||||
public Task DeleteTrack(Track track)
|
||||
{
|
||||
return TrackRepository.Delete(track);
|
||||
}
|
||||
|
||||
public Task DeleteGenre(Genre genre)
|
||||
{
|
||||
return GenreRepository.Delete(genre);
|
||||
}
|
||||
|
||||
public Task DeleteStudio(Studio studio)
|
||||
{
|
||||
return StudioRepository.Delete(studio);
|
||||
}
|
||||
|
||||
public Task DeletePeople(People people)
|
||||
{
|
||||
return PeopleRepository.Delete(people);
|
||||
}
|
||||
|
||||
public Task DeleteLibrary(string library)
|
||||
{
|
||||
return LibraryRepository.Delete(library);
|
||||
}
|
||||
|
||||
public Task DeleteCollection(string collection)
|
||||
{
|
||||
return CollectionRepository.Delete(collection);
|
||||
}
|
||||
|
||||
public Task DeleteShow(string show)
|
||||
{
|
||||
return ShowRepository.Delete(show);
|
||||
}
|
||||
|
||||
public Task DeleteSeason(string season)
|
||||
{
|
||||
return SeasonRepository.Delete(season);
|
||||
}
|
||||
|
||||
public Task DeleteEpisode(string episode)
|
||||
{
|
||||
return EpisodeRepository.Delete(episode);
|
||||
}
|
||||
|
||||
public Task DeleteTrack(string track)
|
||||
{
|
||||
return TrackRepository.Delete(track);
|
||||
}
|
||||
|
||||
public Task DeleteGenre(string genre)
|
||||
{
|
||||
return GenreRepository.Delete(genre);
|
||||
}
|
||||
|
||||
public Task DeleteStudio(string studio)
|
||||
{
|
||||
return StudioRepository.Delete(studio);
|
||||
}
|
||||
|
||||
public Task DeletePeople(string people)
|
||||
{
|
||||
return PeopleRepository.Delete(people);
|
||||
}
|
||||
|
||||
public Task DeleteLibrary(int library)
|
||||
{
|
||||
return LibraryRepository.Delete(library);
|
||||
}
|
||||
|
||||
public Task DeleteCollection(int collection)
|
||||
{
|
||||
return CollectionRepository.Delete(collection);
|
||||
}
|
||||
|
||||
public Task DeleteShow(int show)
|
||||
{
|
||||
return ShowRepository.Delete(show);
|
||||
}
|
||||
|
||||
public Task DeleteSeason(int season)
|
||||
{
|
||||
return SeasonRepository.Delete(season);
|
||||
}
|
||||
|
||||
public Task DeleteEpisode(int episode)
|
||||
{
|
||||
return EpisodeRepository.Delete(episode);
|
||||
}
|
||||
|
||||
public Task DeleteTrack(int track)
|
||||
{
|
||||
return TrackRepository.Delete(track);
|
||||
}
|
||||
|
||||
public Task DeleteGenre(int genre)
|
||||
{
|
||||
return GenreRepository.Delete(genre);
|
||||
}
|
||||
|
||||
public Task DeleteStudio(int studio)
|
||||
{
|
||||
return StudioRepository.Delete(studio);
|
||||
}
|
||||
|
||||
public Task DeletePeople(int people)
|
||||
{
|
||||
return PeopleRepository.Delete(people);
|
||||
return GetRepository<T>().Delete(slug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace Kyoo.Models
|
||||
{
|
||||
[SerializeIgnore] public int ID { 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 virtual Show Show { get; set; }
|
||||
@ -25,7 +25,7 @@ namespace Kyoo.Models
|
||||
|
||||
public MetadataID() { }
|
||||
|
||||
public MetadataID(ProviderID provider, string dataID, string link)
|
||||
public MetadataID(Provider provider, string dataID, string link)
|
||||
{
|
||||
Provider = provider;
|
||||
DataID = dataID;
|
||||
|
@ -11,20 +11,20 @@ namespace Kyoo.Models
|
||||
public string Name { 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<Collection> Collections { get; set; }
|
||||
|
||||
#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, Collection>> CollectionLinks { get; set; }
|
||||
#endif
|
||||
|
||||
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;
|
||||
Name = name;
|
||||
|
@ -3,7 +3,7 @@ using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
public class ProviderID : IResource
|
||||
public class Provider : IResource
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public string Slug { get; set; }
|
||||
@ -13,20 +13,20 @@ namespace Kyoo.Models
|
||||
[LoadableRelation] public virtual ICollection<Library> Libraries { get; set; }
|
||||
|
||||
#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; }
|
||||
#endif
|
||||
|
||||
public ProviderID() { }
|
||||
public Provider() { }
|
||||
|
||||
public ProviderID(string name, string logo)
|
||||
public Provider(string name, string logo)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
Logo = logo;
|
||||
}
|
||||
|
||||
public ProviderID(int id, string name, string logo)
|
||||
public Provider(int id, string name, string logo)
|
||||
{
|
||||
ID = id;
|
||||
Slug = Utility.ToSlug(name);
|
@ -109,18 +109,18 @@ namespace Kyoo.Models
|
||||
if (!ep.Show.IsMovie)
|
||||
{
|
||||
if (ep.EpisodeNumber > 1)
|
||||
previous = await library.GetEpisode(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 1);
|
||||
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 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);
|
||||
previous = await library.GetEpisode(ep.ShowID, ep.SeasonNumber - 1, count);
|
||||
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber - 1, count);
|
||||
}
|
||||
|
||||
if (ep.EpisodeNumber >= await library.GetEpisodesCount(x => x.SeasonID == ep.SeasonID))
|
||||
next = await library.GetEpisode(ep.ShowID, ep.SeasonNumber + 1, 1);
|
||||
if (ep.EpisodeNumber >= await library.GetCount<Episode>(x => x.SeasonID == ep.SeasonID))
|
||||
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber + 1, 1);
|
||||
else
|
||||
next = await library.GetEpisode(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1);
|
||||
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1);
|
||||
}
|
||||
|
||||
return new WatchItem(ep.ID,
|
||||
|
@ -29,22 +29,28 @@ namespace Kyoo.CommonApi
|
||||
[Authorize(Policy = "Read")]
|
||||
public virtual async Task<ActionResult<T>> Get(int id)
|
||||
{
|
||||
T resource = await _repository.Get(id);
|
||||
if (resource == null)
|
||||
try
|
||||
{
|
||||
return await _repository.Get(id);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{slug}")]
|
||||
[Authorize(Policy = "Read")]
|
||||
public virtual async Task<ActionResult<T>> Get(string slug)
|
||||
{
|
||||
T resource = await _repository.Get(slug);
|
||||
if (resource == null)
|
||||
try
|
||||
{
|
||||
return await _repository.Get(slug);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("count")]
|
||||
@ -114,15 +120,19 @@ namespace Kyoo.CommonApi
|
||||
[Authorize(Policy = "Write")]
|
||||
public virtual async Task<ActionResult<T>> Edit([FromQuery] bool resetOld, [FromBody] T resource)
|
||||
{
|
||||
if (resource.ID > 0)
|
||||
try
|
||||
{
|
||||
if (resource.ID > 0)
|
||||
return await _repository.Edit(resource, resetOld);
|
||||
|
||||
T old = await _repository.Get(resource.Slug);
|
||||
resource.ID = old.ID;
|
||||
return await _repository.Edit(resource, resetOld);
|
||||
|
||||
T old = await _repository.Get(resource.Slug);
|
||||
if (old == null)
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
|
||||
resource.ID = old.ID;
|
||||
return await _repository.Edit(resource, resetOld);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id:int}")]
|
||||
@ -144,11 +154,16 @@ namespace Kyoo.CommonApi
|
||||
[Authorize(Policy = "Write")]
|
||||
public virtual async Task<ActionResult<T>> Edit(string slug, [FromQuery] bool resetOld, [FromBody] T resource)
|
||||
{
|
||||
T old = await _repository.Get(slug);
|
||||
if (old == null)
|
||||
try
|
||||
{
|
||||
T old = await _repository.Get(slug);
|
||||
resource.ID = old.ID;
|
||||
return await _repository.Edit(resource, resetOld);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
resource.ID = old.ID;
|
||||
return await _repository.Edit(resource, resetOld);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id:int}")]
|
||||
|
@ -12,52 +12,99 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class to create repositories using Entity Framework.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of this repository</typeparam>
|
||||
public abstract class LocalRepository<T> : IRepository<T>
|
||||
where T : class, IResource
|
||||
{
|
||||
/// <summary>
|
||||
/// The Entity Framework's Database handle.
|
||||
/// </summary>
|
||||
protected readonly DbContext Database;
|
||||
|
||||
/// <summary>
|
||||
/// The default sort order that will be used for this resource's type.
|
||||
/// </summary>
|
||||
protected abstract Expression<Func<T, object>> DefaultSort { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a new base <see cref="LocalRepository{T}"/> with the given database handle.
|
||||
/// </summary>
|
||||
/// <param name="database">A database connection to load resources of type <see cref="T"/></param>
|
||||
protected LocalRepository(DbContext database)
|
||||
{
|
||||
Database = database;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Type RepositoryType => typeof(T);
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource from it's ID and make the <see cref="Database"/> instance track it.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the resource</param>
|
||||
/// <exception cref="ItemNotFound">If the item is not found</exception>
|
||||
/// <returns>The tracked resource with the given ID</returns>
|
||||
protected virtual async Task<T> GetWithTracking(int id)
|
||||
{
|
||||
Database.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
T ret = await Database.Set<T>().AsTracking().FirstOrDefaultAsync(x => x.ID == id);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No {typeof(T).Name} found with the id {id}");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Get(int id)
|
||||
{
|
||||
T ret = await GetOrDefault(id);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No {typeof(T).Name} found with the id {id}");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual ValueTask DisposeAsync()
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Get(string slug)
|
||||
{
|
||||
return Database.DisposeAsync();
|
||||
T ret = await GetOrDefault(slug);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No {typeof(T).Name} found with the slug {slug}");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Get(Expression<Func<T, bool>> where)
|
||||
{
|
||||
T ret = await GetOrDefault(where);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No {typeof(T).Name} found with the given predicate.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual Task<T> Get(int id)
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T> GetOrDefault(int id)
|
||||
{
|
||||
return Database.Set<T>().FirstOrDefaultAsync(x => x.ID == id);
|
||||
}
|
||||
|
||||
public virtual Task<T> GetWithTracking(int id)
|
||||
{
|
||||
return Database.Set<T>().AsTracking().FirstOrDefaultAsync(x => x.ID == id);
|
||||
}
|
||||
|
||||
public virtual Task<T> Get(string slug)
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T> GetOrDefault(string slug)
|
||||
{
|
||||
return Database.Set<T>().FirstOrDefaultAsync(x => x.Slug == slug);
|
||||
}
|
||||
|
||||
public virtual Task<T> Get(Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
return Database.Set<T>().FirstOrDefaultAsync(predicate);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T> GetOrDefault(Expression<Func<T, bool>> where)
|
||||
{
|
||||
return Database.Set<T>().FirstOrDefaultAsync(where);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task<ICollection<T>> Search(string query);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
@ -65,6 +112,14 @@ namespace Kyoo.Controllers
|
||||
return ApplyFilters(Database.Set<T>(), where, sort, limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply filters to a query to ease sort, pagination & where queries for resources of this repository
|
||||
/// </summary>
|
||||
/// <param name="query">The base query to filter.</param>
|
||||
/// <param name="where">An expression to filter based on arbitrary conditions</param>
|
||||
/// <param name="sort">The sort settings (sort order & sort by)</param>
|
||||
/// <param name="limit">Paginations information (where to start and how many to get)</param>
|
||||
/// <returns>The filtered query</returns>
|
||||
protected Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
|
||||
Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
@ -73,6 +128,17 @@ namespace Kyoo.Controllers
|
||||
return ApplyFilters(query, Get, DefaultSort, where, sort, limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply filters to a query to ease sort, pagination & where queries for any resources types.
|
||||
/// For resources of type <see cref="T"/>, see <see cref="ApplyFilters"/>
|
||||
/// </summary>
|
||||
/// <param name="get">A function to asynchronously get a resource from the database using it's ID.</param>
|
||||
/// <param name="defaultSort">The default sort order of this resource's type.</param>
|
||||
/// <param name="query">The base query to filter.</param>
|
||||
/// <param name="where">An expression to filter based on arbitrary conditions</param>
|
||||
/// <param name="sort">The sort settings (sort order & sort by)</param>
|
||||
/// <param name="limit">Paginations information (where to start and how many to get)</param>
|
||||
/// <returns>The filtered query</returns>
|
||||
protected async Task<ICollection<TValue>> ApplyFilters<TValue>(IQueryable<TValue> query,
|
||||
Func<int, Task<TValue>> get,
|
||||
Expression<Func<TValue, object>> defaultSort,
|
||||
@ -108,6 +174,7 @@ namespace Kyoo.Controllers
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual Task<int> GetCount(Expression<Func<T, bool>> where = null)
|
||||
{
|
||||
IQueryable<T> query = Database.Set<T>();
|
||||
@ -116,6 +183,7 @@ namespace Kyoo.Controllers
|
||||
return query.CountAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Create(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
@ -124,6 +192,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> CreateIfNotExists(T obj, bool silentFail = false)
|
||||
{
|
||||
try
|
||||
@ -139,10 +208,7 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
catch (DuplicatedItemException)
|
||||
{
|
||||
T old = await Get(obj!.Slug);
|
||||
if (old == null)
|
||||
throw new SystemException("Unknown database state.");
|
||||
return old;
|
||||
return await Get(obj.Slug);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -152,6 +218,7 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Edit(T edited, bool resetOld)
|
||||
{
|
||||
if (edited == null)
|
||||
@ -162,9 +229,7 @@ namespace Kyoo.Controllers
|
||||
try
|
||||
{
|
||||
T old = await GetWithTracking(edited.ID);
|
||||
if (old == null)
|
||||
throw new ItemNotFound($"No resource found with the ID {edited.ID}.");
|
||||
|
||||
|
||||
if (resetOld)
|
||||
Utility.Nullify(old);
|
||||
Utility.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null);
|
||||
@ -178,11 +243,24 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An overridable method to edit relatiosn of a resource.
|
||||
/// </summary>
|
||||
/// <param name="resource">The non edited resource</param>
|
||||
/// <param name="changed">The new version of <see cref="resource"/>. This item will be saved on the databse and replace <see cref="resource"/></param>
|
||||
/// <param name="resetOld">A boolean to indicate if all values of resource should be discarded or not.</param>
|
||||
/// <returns></returns>
|
||||
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
|
||||
{
|
||||
return Validate(resource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method called just before saving a new resource to the database.
|
||||
/// It is also called on the default implementation of <see cref="EditRelations"/>
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource that will be saved</param>
|
||||
/// <exception cref="ArgumentException">You can throw this if the resource is illegal and should not be saved.</exception>
|
||||
protected virtual Task Validate(T resource)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
@ -205,38 +283,45 @@ namespace Kyoo.Controllers
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task Delete(int id)
|
||||
{
|
||||
T resource = await Get(id);
|
||||
await Delete(resource);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task Delete(string slug)
|
||||
{
|
||||
T resource = await Get(slug);
|
||||
await Delete(resource);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task Delete(T obj);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task DeleteRange(IEnumerable<T> objs)
|
||||
{
|
||||
foreach (T obj in objs)
|
||||
await Delete(obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task DeleteRange(IEnumerable<int> ids)
|
||||
{
|
||||
foreach (int id in ids)
|
||||
await Delete(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task DeleteRange(IEnumerable<string> slugs)
|
||||
{
|
||||
foreach (string slug in slugs)
|
||||
await Delete(slug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task DeleteRange(Expression<Func<T, bool>> where)
|
||||
{
|
||||
ICollection<T> resources = await GetAll(where);
|
||||
|
@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Kyoo.CommonApi
|
||||
{
|
||||
@ -77,7 +76,7 @@ namespace Kyoo.CommonApi
|
||||
if (result.DeclaredType == null)
|
||||
return;
|
||||
|
||||
await using ILibraryManager library = context.HttpContext.RequestServices.GetService<ILibraryManager>();
|
||||
ILibraryManager library = context.HttpContext.RequestServices.GetService<ILibraryManager>();
|
||||
ICollection<string> fields = (ICollection<string>)context.HttpContext.Items["fields"];
|
||||
Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>));
|
||||
|
||||
|
@ -10,6 +10,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.0.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
|
20
Kyoo.Tests/Library/SetupTests.cs
Normal file
20
Kyoo.Tests/Library/SetupTests.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Kyoo.Tests
|
||||
{
|
||||
public class SetupTests
|
||||
{
|
||||
// TODO test libraries & repositories via a on-memory SQLite database.
|
||||
// TODO Requires: Kyoo should be database agonistic and database implementations should be available via a plugin.
|
||||
|
||||
// [Fact]
|
||||
// public void Get_Test()
|
||||
// {
|
||||
// TestContext context = new();
|
||||
// using DatabaseContext database = context.New();
|
||||
//
|
||||
// Assert.Equal(1, database.Shows.Count());
|
||||
// }
|
||||
}
|
||||
}
|
79
Kyoo.Tests/Library/TestContext.cs
Normal file
79
Kyoo.Tests/Library/TestContext.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Kyoo.Models;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Class responsible to fill and create in memory databases for unit tests.
|
||||
/// </summary>
|
||||
public class TestContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The context's options that specify to use an in memory Sqlite database.
|
||||
/// </summary>
|
||||
private readonly DbContextOptions<DatabaseContext> _context;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new database and fill it with informations.
|
||||
/// </summary>
|
||||
public TestContext()
|
||||
{
|
||||
SqliteConnection connection = new("DataSource=:memory:");
|
||||
connection.Open();
|
||||
|
||||
try
|
||||
{
|
||||
_context = new DbContextOptionsBuilder<DatabaseContext>()
|
||||
.UseSqlite(connection)
|
||||
.Options;
|
||||
FillDatabase();
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the database with pre defined values using a clean context.
|
||||
/// </summary>
|
||||
private void FillDatabase()
|
||||
{
|
||||
using DatabaseContext context = new(_context);
|
||||
context.Shows.Add(new Show
|
||||
{
|
||||
ID = 67,
|
||||
Slug = "anohana",
|
||||
Title = "Anohana: The Flower We Saw That Day",
|
||||
Aliases = new[]
|
||||
{
|
||||
"Ano Hi Mita Hana no Namae o Bokutachi wa Mada Shiranai.",
|
||||
"AnoHana",
|
||||
"We Still Don't Know the Name of the Flower We Saw That Day."
|
||||
},
|
||||
Overview = "When Yadomi Jinta was a child, he was a central piece in a group of close friends. " +
|
||||
"In time, however, these childhood friends drifted apart, and when they became high " +
|
||||
"school students, they had long ceased to think of each other as friends.",
|
||||
Status = Status.Finished,
|
||||
TrailerUrl = null,
|
||||
StartYear = 2011,
|
||||
EndYear = 2011,
|
||||
Poster = "poster",
|
||||
Logo = "logo",
|
||||
Backdrop = "backdrop",
|
||||
IsMovie = false,
|
||||
Studio = null
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a new databse context connected to a in memory Sqlite databse.
|
||||
/// </summary>
|
||||
/// <returns>A valid DatabaseContext</returns>
|
||||
public DatabaseContext New()
|
||||
{
|
||||
return new(_context);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,34 +8,30 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle collections
|
||||
/// </summary>
|
||||
public class CollectionRepository : LocalRepository<Collection>, ICollectionRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Collection, object>> DefaultSort => x => x.Name;
|
||||
|
||||
public CollectionRepository(DatabaseContext database) : base(database)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="CollectionRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle to use</param>
|
||||
public CollectionRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Collection>> Search(string query)
|
||||
{
|
||||
return await _database.Collections
|
||||
@ -45,6 +41,7 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Collection> Create(Collection obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -53,6 +50,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Collection obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -5,21 +5,45 @@ using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle episodes.
|
||||
/// </summary>
|
||||
public class EpisodeRepository : LocalRepository<Episode>, IEpisodeRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The databse handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
/// <summary>
|
||||
/// A show repository to get show's slug from their ID and keep the slug in each episode.
|
||||
/// </summary>
|
||||
private readonly IShowRepository _shows;
|
||||
/// <summary>
|
||||
/// A track repository to handle creation and deletion of tracks related to the current episode.
|
||||
/// </summary>
|
||||
private readonly ITrackRepository _tracks;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Episode, object>> DefaultSort => x => x.EpisodeNumber;
|
||||
|
||||
|
||||
public EpisodeRepository(DatabaseContext database,
|
||||
/// <summary>
|
||||
/// Create a new <see cref="EpisodeRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle to use.</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
/// <param name="shows">A show repository</param>
|
||||
/// <param name="tracks">A track repository</param>
|
||||
public EpisodeRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
IShowRepository shows,
|
||||
ITrackRepository tracks)
|
||||
@ -30,62 +54,46 @@ namespace Kyoo.Controllers
|
||||
_shows = shows;
|
||||
_tracks = tracks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
/// <inheritdoc />
|
||||
public override async Task<Episode> GetOrDefault(int id)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
_shows.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
await _shows.DisposeAsync();
|
||||
}
|
||||
|
||||
public override async Task<Episode> Get(int id)
|
||||
{
|
||||
Episode ret = await base.Get(id);
|
||||
Episode ret = await base.GetOrDefault(id);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override async Task<Episode> Get(string slug)
|
||||
/// <inheritdoc />
|
||||
public override async Task<Episode> GetOrDefault(string slug)
|
||||
{
|
||||
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)e(?<episode>\d*)");
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
return await Get(match.Groups["show"].Value,
|
||||
return await GetOrDefault(match.Groups["show"].Value,
|
||||
int.Parse(match.Groups["season"].Value),
|
||||
int.Parse(match.Groups["episode"].Value));
|
||||
}
|
||||
|
||||
Episode episode = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == slug);
|
||||
episode.ShowSlug = slug;
|
||||
if (episode != null)
|
||||
episode.ShowSlug = slug;
|
||||
return episode;
|
||||
}
|
||||
|
||||
public override async Task<Episode> Get(Expression<Func<Episode, bool>> predicate)
|
||||
/// <inheritdoc />
|
||||
public override async Task<Episode> GetOrDefault(Expression<Func<Episode, bool>> where)
|
||||
{
|
||||
Episode ret = await base.Get(predicate);
|
||||
Episode ret = await base.GetOrDefault(where);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
@ -95,7 +103,26 @@ namespace Kyoo.Controllers
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> Get(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
Episode ret = await GetOrDefault(showID, seasonNumber, episodeNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No episode S{seasonNumber}E{episodeNumber} found on the show {showID}.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
Episode ret = await GetOrDefault(showSlug, seasonNumber, episodeNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No episode S{seasonNumber}E{episodeNumber} found on the show {showSlug}.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
@ -105,15 +132,7 @@ namespace Kyoo.Controllers
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<Episode> Get(int seasonID, int episodeNumber)
|
||||
{
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.SeasonID == seasonID
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
{
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
@ -123,6 +142,7 @@ namespace Kyoo.Controllers
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
{
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
@ -132,6 +152,7 @@ namespace Kyoo.Controllers
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Episode>> Search(string query)
|
||||
{
|
||||
List<Episode> episodes = await _database.Episodes
|
||||
@ -144,6 +165,7 @@ namespace Kyoo.Controllers
|
||||
return episodes;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Episode>> GetAll(Expression<Func<Episode, bool>> where = null,
|
||||
Sort<Episode> sort = default,
|
||||
Pagination limit = default)
|
||||
@ -154,6 +176,7 @@ namespace Kyoo.Controllers
|
||||
return episodes;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Episode> Create(Episode obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -163,6 +186,7 @@ namespace Kyoo.Controllers
|
||||
return await ValidateTracks(obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Episode resource, Episode changed, bool resetOld)
|
||||
{
|
||||
if (resource.ShowID <= 0)
|
||||
@ -184,6 +208,11 @@ namespace Kyoo.Controllers
|
||||
await Validate(resource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set track's index and ensure that every tracks is well-formed.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource to fix.</param>
|
||||
/// <returns>The <see cref="resource"/> parameter is returnned.</returns>
|
||||
private async Task<Episode> ValidateTracks(Episode resource)
|
||||
{
|
||||
resource.Tracks = await resource.Tracks.MapAsync((x, i) =>
|
||||
@ -198,6 +227,7 @@ namespace Kyoo.Controllers
|
||||
return resource;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task Validate(Episode resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
@ -209,13 +239,8 @@ namespace Kyoo.Controllers
|
||||
return x;
|
||||
}).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task Delete(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
Episode obj = await Get(showSlug, seasonNumber, episodeNumber);
|
||||
await Delete(obj);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Episode obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -8,35 +8,31 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository for genres.
|
||||
/// </summary>
|
||||
public class GenreRepository : LocalRepository<Genre>, IGenreRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Genre, object>> DefaultSort => x => x.Slug;
|
||||
|
||||
|
||||
public GenreRepository(DatabaseContext database) : base(database)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="GenreRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
public GenreRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Genre>> Search(string query)
|
||||
{
|
||||
return await _database.Genres
|
||||
@ -46,6 +42,7 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Genre> Create(Genre obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -54,6 +51,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Genre obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -10,18 +10,45 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle library items.
|
||||
/// </summary>
|
||||
public class LibraryItemRepository : LocalRepository<LibraryItem>, ILibraryItemRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
/// <summary>
|
||||
/// A lazy loaded library repository to validate queries (check if a library does exist)
|
||||
/// </summary>
|
||||
private readonly Lazy<ILibraryRepository> _libraries;
|
||||
/// <summary>
|
||||
/// A lazy loaded show repository to get a show from it's id.
|
||||
/// </summary>
|
||||
private readonly Lazy<IShowRepository> _shows;
|
||||
/// <summary>
|
||||
/// A lazy loaded collection repository to get a collection from it's id.
|
||||
/// </summary>
|
||||
private readonly Lazy<ICollectionRepository> _collections;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<LibraryItem, object>> DefaultSort => x => x.Title;
|
||||
|
||||
|
||||
public LibraryItemRepository(DatabaseContext database, IProviderRepository providers, IServiceProvider services)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="LibraryItemRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The databse instance</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
/// <param name="services">A service provider to lazilly request a library, show or collection repository.</param>
|
||||
public LibraryItemRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
IServiceProvider services)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
@ -30,53 +57,34 @@ namespace Kyoo.Controllers
|
||||
_shows = new Lazy<IShowRepository>(services.GetRequiredService<IShowRepository>);
|
||||
_collections = new Lazy<ICollectionRepository>(services.GetRequiredService<ICollectionRepository>);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
if (_shows.IsValueCreated)
|
||||
_shows.Value.Dispose();
|
||||
if (_collections.IsValueCreated)
|
||||
_collections.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
if (_shows.IsValueCreated)
|
||||
await _shows.Value.DisposeAsync();
|
||||
if (_collections.IsValueCreated)
|
||||
await _collections.Value.DisposeAsync();
|
||||
}
|
||||
|
||||
public override async Task<LibraryItem> Get(int id)
|
||||
/// <inheritdoc />
|
||||
public override async Task<LibraryItem> GetOrDefault(int id)
|
||||
{
|
||||
return id > 0
|
||||
? new LibraryItem(await _shows.Value.Get(id))
|
||||
: new LibraryItem(await _collections.Value.Get(-id));
|
||||
? new LibraryItem(await _shows.Value.GetOrDefault(id))
|
||||
: new LibraryItem(await _collections.Value.GetOrDefault(-id));
|
||||
}
|
||||
|
||||
public override Task<LibraryItem> Get(string slug)
|
||||
/// <inheritdoc />
|
||||
public override Task<LibraryItem> GetOrDefault(string slug)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
throw new InvalidOperationException("You can't get a library item by a slug.");
|
||||
}
|
||||
|
||||
private IQueryable<LibraryItem> ItemsQuery
|
||||
/// <summary>
|
||||
/// Get a basic queryable with the right mapping from shows & collections.
|
||||
/// Shows contained in a collection are excluded.
|
||||
/// </summary>
|
||||
private IQueryable<LibraryItem> ItemsQuery
|
||||
=> _database.Shows
|
||||
.Where(x => !x.Collections.Any())
|
||||
.Select(LibraryItem.FromShow)
|
||||
.Concat(_database.Collections
|
||||
.Select(LibraryItem.FromCollection));
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<ICollection<LibraryItem>> GetAll(Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default)
|
||||
@ -84,6 +92,7 @@ namespace Kyoo.Controllers
|
||||
return ApplyFilters(ItemsQuery, where, sort, limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<int> GetCount(Expression<Func<LibraryItem, bool>> where = null)
|
||||
{
|
||||
IQueryable<LibraryItem> query = ItemsQuery;
|
||||
@ -92,6 +101,7 @@ namespace Kyoo.Controllers
|
||||
return query.CountAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<LibraryItem>> Search(string query)
|
||||
{
|
||||
return await ItemsQuery
|
||||
@ -101,19 +111,31 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<LibraryItem> Create(LibraryItem obj) => throw new InvalidOperationException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<LibraryItem> CreateIfNotExists(LibraryItem obj, bool silentFail = false)
|
||||
{
|
||||
if (silentFail)
|
||||
return Task.FromResult<LibraryItem>(default);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public override Task<LibraryItem> Edit(LibraryItem obj, bool reset) => throw new InvalidOperationException();
|
||||
/// <inheritdoc />
|
||||
public override Task Delete(int id) => throw new InvalidOperationException();
|
||||
/// <inheritdoc />
|
||||
public override Task Delete(string slug) => throw new InvalidOperationException();
|
||||
/// <inheritdoc />
|
||||
public override Task Delete(LibraryItem obj) => throw new InvalidOperationException();
|
||||
|
||||
/// <summary>
|
||||
/// Get a basic queryable for a library with the right mapping from shows & collections.
|
||||
/// Shows contained in a collection are excluded.
|
||||
/// </summary>
|
||||
/// <param name="selector">Only items that are part of a library that match this predicate will be returned.</param>
|
||||
/// <returns>A queryable containing items that are part of a library matching the selector.</returns>
|
||||
private IQueryable<LibraryItem> LibraryRelatedQuery(Expression<Func<Library, bool>> selector)
|
||||
=> _database.Libraries
|
||||
.Where(selector)
|
||||
@ -125,7 +147,8 @@ namespace Kyoo.Controllers
|
||||
.SelectMany(x => x.Collections)
|
||||
.Select(LibraryItem.FromCollection));
|
||||
|
||||
public async Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default)
|
||||
@ -134,12 +157,13 @@ namespace Kyoo.Controllers
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
if (!items.Any() && await _libraries.Value.Get(id) == null)
|
||||
if (!items.Any() && await _libraries.Value.GetOrDefault(id) == null)
|
||||
throw new ItemNotFound();
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default)
|
||||
@ -148,7 +172,7 @@ namespace Kyoo.Controllers
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
if (!items.Any() && await _libraries.Value.Get(slug) == null)
|
||||
if (!items.Any() && await _libraries.Value.GetOrDefault(slug) == null)
|
||||
throw new ItemNotFound();
|
||||
return items;
|
||||
}
|
||||
|
@ -8,40 +8,38 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle libraries.
|
||||
/// </summary>
|
||||
public class LibraryRepository : LocalRepository<Library>, ILibraryRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Library, object>> DefaultSort => x => x.ID;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="LibraryRepository"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
/// <param name="providers">The providere repository</param>
|
||||
public LibraryRepository(DatabaseContext database, IProviderRepository providers)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Library>> Search(string query)
|
||||
{
|
||||
return await _database.Libraries
|
||||
@ -51,6 +49,7 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Library> Create(Library obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -61,6 +60,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task Validate(Library resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
@ -69,6 +69,7 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Library resource, Library changed, bool resetOld)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
@ -86,6 +87,7 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Library obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -10,46 +10,45 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle people.
|
||||
/// </summary>
|
||||
public class PeopleRepository : LocalRepository<People>, IPeopleRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
/// <summary>
|
||||
/// A lazy loaded show repository to validate requests from shows.
|
||||
/// </summary>
|
||||
private readonly Lazy<IShowRepository> _shows;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<People, object>> DefaultSort => x => x.Name;
|
||||
|
||||
public PeopleRepository(DatabaseContext database, IProviderRepository providers, IServiceProvider services)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="PeopleRepository"/>
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
/// <param name="services">A service provider to lazy load a show repository</param>
|
||||
public PeopleRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
IServiceProvider services)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
_shows = new Lazy<IShowRepository>(services.GetRequiredService<IShowRepository>);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
if (_shows.IsValueCreated)
|
||||
_shows.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
if (_shows.IsValueCreated)
|
||||
await _shows.Value.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<People>> Search(string query)
|
||||
{
|
||||
return await _database.People
|
||||
@ -59,6 +58,7 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<People> Create(People obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -68,6 +68,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task Validate(People resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
@ -85,6 +86,7 @@ namespace Kyoo.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(People resource, People changed, bool resetOld)
|
||||
{
|
||||
if (changed.Roles != null || resetOld)
|
||||
@ -102,6 +104,7 @@ namespace Kyoo.Controllers
|
||||
await base.EditRelations(resource, changed, resetOld);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(People obj)
|
||||
{
|
||||
if (obj == null)
|
||||
@ -113,6 +116,7 @@ namespace Kyoo.Controllers
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<PeopleRole>> GetFromShow(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
@ -133,6 +137,7 @@ namespace Kyoo.Controllers
|
||||
return people;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
@ -154,24 +159,26 @@ namespace Kyoo.Controllers
|
||||
return people;
|
||||
}
|
||||
|
||||
public async Task<ICollection<PeopleRole>> GetFromPeople(int peopleID,
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<PeopleRole>> GetFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
|
||||
.Where(x => x.PeopleID == peopleID)
|
||||
.Where(x => x.PeopleID == id)
|
||||
.Include(x => x.Show),
|
||||
id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
|
||||
y => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == y),
|
||||
x => x.Show.Title,
|
||||
where,
|
||||
sort,
|
||||
limit);
|
||||
if (!roles.Any() && await Get(peopleID) == null)
|
||||
if (!roles.Any() && await Get(id) == null)
|
||||
throw new ItemNotFound();
|
||||
return roles;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ICollection<PeopleRole>> GetFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
|
@ -8,18 +8,32 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
public class ProviderRepository : LocalRepository<ProviderID>, IProviderRepository
|
||||
/// <summary>
|
||||
/// A local repository to handle providers.
|
||||
/// </summary>
|
||||
public class ProviderRepository : LocalRepository<Provider>, IProviderRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
protected override Expression<Func<ProviderID, object>> DefaultSort => x => x.Slug;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Provider, object>> DefaultSort => x => x.Slug;
|
||||
|
||||
|
||||
public ProviderRepository(DatabaseContext database) : base(database)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ProviderRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
public ProviderRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<ProviderID>> Search(string query)
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Provider>> Search(string query)
|
||||
{
|
||||
return await _database.Providers
|
||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||
@ -28,7 +42,8 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public override async Task<ProviderID> Create(ProviderID obj)
|
||||
/// <inheritdoc />
|
||||
public override async Task<Provider> Create(Provider obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
@ -36,7 +51,8 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
public override async Task Delete(ProviderID obj)
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Provider obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
@ -46,6 +62,7 @@ namespace Kyoo.Controllers
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null,
|
||||
Sort<MetadataID> sort = default,
|
||||
Pagination limit = default)
|
||||
|
@ -5,22 +5,47 @@ using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle seasons.
|
||||
/// </summary>
|
||||
public class SeasonRepository : LocalRepository<Season>, ISeasonRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
/// <summary>
|
||||
/// A show repository to get show's slug from their ID and keep the slug in each episode.
|
||||
/// </summary>
|
||||
private readonly IShowRepository _shows;
|
||||
/// <summary>
|
||||
/// A lazilly loaded episode repository to handle deletion of episodes with the season.
|
||||
/// </summary>
|
||||
private readonly Lazy<IEpisodeRepository> _episodes;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber;
|
||||
|
||||
|
||||
public SeasonRepository(DatabaseContext database,
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SeasonRepository"/> using the provided handle, a provider & a show repository and
|
||||
/// a service provider to lazilly request an episode repository.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle that will be used</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
/// <param name="shows">A show repository</param>
|
||||
/// <param name="services">A service provider to lazilly request an episode repository.</param>
|
||||
public SeasonRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
IShowRepository shows,
|
||||
IServiceProvider services)
|
||||
@ -31,49 +56,25 @@ namespace Kyoo.Controllers
|
||||
_shows = shows;
|
||||
_episodes = new Lazy<IEpisodeRepository>(services.GetRequiredService<IEpisodeRepository>);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
_shows.Dispose();
|
||||
if (_episodes.IsValueCreated)
|
||||
_episodes.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
await _shows.DisposeAsync();
|
||||
if (_episodes.IsValueCreated)
|
||||
await _episodes.Value.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<Season> Get(int id)
|
||||
{
|
||||
Season ret = await base.Get(id);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override async Task<Season> Get(Expression<Func<Season, bool>> predicate)
|
||||
/// <inheritdoc/>
|
||||
public override async Task<Season> Get(Expression<Func<Season, bool>> where)
|
||||
{
|
||||
Season ret = await base.Get(predicate);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
Season ret = await base.Get(where);
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task<Season> Get(string slug)
|
||||
{
|
||||
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)");
|
||||
@ -83,24 +84,41 @@ namespace Kyoo.Controllers
|
||||
return Get(match.Groups["show"].Value, int.Parse(match.Groups["season"].Value));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Season> Get(int showID, int seasonNumber)
|
||||
{
|
||||
Season ret = await _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
Season ret = await GetOrDefault(showID, seasonNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No season {seasonNumber} found for the show {showID}");
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Season> Get(string showSlug, int seasonNumber)
|
||||
{
|
||||
Season ret = await _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = showSlug;
|
||||
Season ret = await GetOrDefault(showSlug, seasonNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No season {seasonNumber} found for the show {showSlug}");
|
||||
ret.ShowSlug = showSlug;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<Season> GetOrDefault(int showID, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<Season> GetOrDefault(string showSlug, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<ICollection<Season>> Search(string query)
|
||||
{
|
||||
List<Season> seasons = await _database.Seasons
|
||||
@ -113,6 +131,7 @@ namespace Kyoo.Controllers
|
||||
return seasons;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<ICollection<Season>> GetAll(Expression<Func<Season, bool>> where = null,
|
||||
Sort<Season> sort = default,
|
||||
Pagination limit = default)
|
||||
@ -123,6 +142,7 @@ namespace Kyoo.Controllers
|
||||
return seasons;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<Season> Create(Season obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -132,6 +152,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task Validate(Season resource)
|
||||
{
|
||||
if (resource.ShowID <= 0)
|
||||
@ -146,6 +167,7 @@ namespace Kyoo.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task EditRelations(Season resource, Season changed, bool resetOld)
|
||||
{
|
||||
if (changed.ExternalIDs != null || resetOld)
|
||||
@ -155,13 +177,8 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
await base.EditRelations(resource, changed, resetOld);
|
||||
}
|
||||
|
||||
public async Task Delete(string showSlug, int seasonNumber)
|
||||
{
|
||||
Season obj = await Get(showSlug, seasonNumber);
|
||||
await Delete(obj);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task Delete(Season obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -9,18 +9,52 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle shows
|
||||
/// </summary>
|
||||
public class ShowRepository : LocalRepository<Show>, IShowRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The databse handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
/// <summary>
|
||||
/// A studio repository to handle creation/validation of related studios.
|
||||
/// </summary>
|
||||
private readonly IStudioRepository _studios;
|
||||
/// <summary>
|
||||
/// A people repository to handle creation/validation of related people.
|
||||
/// </summary>
|
||||
private readonly IPeopleRepository _people;
|
||||
/// <summary>
|
||||
/// A genres repository to handle creation/validation of related genres.
|
||||
/// </summary>
|
||||
private readonly IGenreRepository _genres;
|
||||
/// <summary>
|
||||
/// A provider repository to handle externalID creation and deletion
|
||||
/// </summary>
|
||||
private readonly IProviderRepository _providers;
|
||||
/// <summary>
|
||||
/// A lazy loaded season repository to handle cascade deletion (seasons deletion whith it's show)
|
||||
/// </summary>
|
||||
private readonly Lazy<ISeasonRepository> _seasons;
|
||||
/// <summary>
|
||||
/// A lazy loaded episode repository to handle cascade deletion (episode deletion whith it's show)
|
||||
/// </summary>
|
||||
private readonly Lazy<IEpisodeRepository> _episodes;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Show, object>> DefaultSort => x => x.Title;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ShowRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle to use</param>
|
||||
/// <param name="studios">A studio repository</param>
|
||||
/// <param name="people">A people repository</param>
|
||||
/// <param name="genres">A genres repository</param>
|
||||
/// <param name="providers">A provider repository</param>
|
||||
/// <param name="services">A service provider to lazilly request a season and an episode repository</param>
|
||||
public ShowRepository(DatabaseContext database,
|
||||
IStudioRepository studios,
|
||||
IPeopleRepository people,
|
||||
@ -37,40 +71,9 @@ namespace Kyoo.Controllers
|
||||
_seasons = new Lazy<ISeasonRepository>(services.GetRequiredService<ISeasonRepository>);
|
||||
_episodes = new Lazy<IEpisodeRepository>(services.GetRequiredService<IEpisodeRepository>);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_studios.Dispose();
|
||||
_people.Dispose();
|
||||
_genres.Dispose();
|
||||
_providers.Dispose();
|
||||
if (_seasons.IsValueCreated)
|
||||
_seasons.Value.Dispose();
|
||||
if (_episodes.IsValueCreated)
|
||||
_episodes.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _studios.DisposeAsync();
|
||||
await _people.DisposeAsync();
|
||||
await _genres.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
if (_seasons.IsValueCreated)
|
||||
await _seasons.Value.DisposeAsync();
|
||||
if (_episodes.IsValueCreated)
|
||||
await _episodes.Value.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Show>> Search(string query)
|
||||
{
|
||||
query = $"%{query}%";
|
||||
@ -83,6 +86,7 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Show> Create(Show obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -94,6 +98,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task Validate(Show resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
@ -119,6 +124,7 @@ namespace Kyoo.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task EditRelations(Show resource, Show changed, bool resetOld)
|
||||
{
|
||||
await Validate(changed);
|
||||
@ -145,6 +151,7 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task AddShowLink(int showID, int? libraryID, int? collectionID)
|
||||
{
|
||||
if (collectionID != null)
|
||||
@ -168,6 +175,7 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<string> GetSlug(int showID)
|
||||
{
|
||||
return _database.Shows.Where(x => x.ID == showID)
|
||||
@ -175,6 +183,7 @@ namespace Kyoo.Controllers
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Show obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -8,17 +8,31 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle studios
|
||||
/// </summary>
|
||||
public class StudioRepository : LocalRepository<Studio>, IStudioRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// The database handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Studio, object>> DefaultSort => x => x.Name;
|
||||
|
||||
|
||||
public StudioRepository(DatabaseContext database) : base(database)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="StudioRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The database handle</param>
|
||||
public StudioRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ICollection<Studio>> Search(string query)
|
||||
{
|
||||
return await _database.Studios
|
||||
@ -28,6 +42,7 @@ namespace Kyoo.Controllers
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Studio> Create(Studio obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
@ -36,6 +51,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Studio obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@ -9,40 +10,48 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A local repository to handle tracks.
|
||||
/// </summary>
|
||||
public class TrackRepository : LocalRepository<Track>, ITrackRepository
|
||||
{
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// The databse handle
|
||||
/// </summary>
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Expression<Func<Track, object>> DefaultSort => x => x.TrackIndex;
|
||||
|
||||
|
||||
public TrackRepository(DatabaseContext database) : base(database)
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TrackRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">The datatabse handle</param>
|
||||
public TrackRepository(DatabaseContext database)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
/// <inheritdoc />
|
||||
Task<Track> IRepository<Track>.Get(string slug)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
return Get(slug);
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
/// <inheritdoc />
|
||||
public async Task<Track> Get(string slug, StreamType type = StreamType.Unknown)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
}
|
||||
|
||||
public override Task<Track> Get(string slug)
|
||||
{
|
||||
return Get(slug, StreamType.Unknown);
|
||||
Track ret = await GetOrDefault(slug, type);
|
||||
if (ret == null)
|
||||
throw new ItemNotFound($"No track found with the slug {slug} and the type {type}.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Track> Get(string slug, StreamType type)
|
||||
/// <inheritdoc />
|
||||
public Task<Track> GetOrDefault(string slug, StreamType type = StreamType.Unknown)
|
||||
{
|
||||
Match match = Regex.Match(slug,
|
||||
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)(\.(?<type>\w*))?\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||
@ -65,27 +74,23 @@ namespace Kyoo.Controllers
|
||||
if (match.Groups["type"].Success)
|
||||
type = Enum.Parse<StreamType>(match.Groups["type"].Value, true);
|
||||
|
||||
if (type == StreamType.Unknown)
|
||||
{
|
||||
return _database.Tracks.FirstOrDefaultAsync(x => x.Episode.Show.Slug == showSlug
|
||||
&& x.Episode.SeasonNumber == seasonNumber
|
||||
&& x.Episode.EpisodeNumber == episodeNumber
|
||||
&& x.Language == language
|
||||
&& x.IsForced == forced);
|
||||
}
|
||||
return _database.Tracks.FirstOrDefaultAsync(x => x.Episode.Show.Slug == showSlug
|
||||
&& x.Episode.SeasonNumber == seasonNumber
|
||||
&& x.Episode.EpisodeNumber == episodeNumber
|
||||
&& x.Type == type
|
||||
&& x.Language == language
|
||||
&& x.IsForced == forced);
|
||||
IQueryable<Track> query = _database.Tracks.Where(x => x.Episode.Show.Slug == showSlug
|
||||
&& x.Episode.SeasonNumber == seasonNumber
|
||||
&& x.Episode.EpisodeNumber == episodeNumber
|
||||
&& x.Language == language
|
||||
&& x.IsForced == forced);
|
||||
if (type != StreamType.Unknown)
|
||||
return query.FirstOrDefaultAsync(x => x.Type == type);
|
||||
return query.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<ICollection<Track>> Search(string query)
|
||||
{
|
||||
throw new InvalidOperationException("Tracks do not support the search method.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Track> Create(Track obj)
|
||||
{
|
||||
if (obj.EpisodeID <= 0)
|
||||
@ -107,6 +112,7 @@ namespace Kyoo.Controllers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(Track obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -92,7 +92,7 @@ namespace Kyoo.Controllers
|
||||
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)
|
||||
return;
|
||||
@ -145,7 +145,7 @@ namespace Kyoo.Controllers
|
||||
return Task.FromResult(thumbPath.StartsWith(_peoplePath) ? thumbPath : null);
|
||||
}
|
||||
|
||||
public Task<string> GetProviderLogo(ProviderID provider)
|
||||
public Task<string> GetProviderLogo(Provider provider)
|
||||
{
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException(nameof(provider));
|
||||
|
@ -113,10 +113,8 @@
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildTranscoder" BeforeTargets="BeforeBuild" Condition="'$(SkipTranscoder)' != 'true'">
|
||||
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' != 'true'"
|
||||
Command="mkdir -p build %26%26 cd build %26%26 cmake .. %26%26 make -j" />
|
||||
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' == 'true'"
|
||||
Command='(if not exist build mkdir build) %26%26 cd build %26%26 cmake .. -G "NMake Makefiles" %26%26 nmake' />
|
||||
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' != 'true'" Command="mkdir -p build %26%26 cd build %26%26 cmake .. %26%26 make -j" />
|
||||
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' == 'true'" Command="(if not exist build mkdir build) %26%26 cd build %26%26 cmake .. -G "NMake Makefiles" %26%26 nmake" />
|
||||
<Copy SourceFiles="$(TranscoderRoot)/build/$(TranscoderBinary)" DestinationFolder="." />
|
||||
</Target>
|
||||
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -10,28 +11,71 @@ using Npgsql;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
/// <summary>
|
||||
/// The database handle used for all local repositories.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It should not be used directly, to access the database use a <see cref="ILibraryManager"/> or repositories.
|
||||
/// </remarks>
|
||||
public class DatabaseContext : DbContext
|
||||
{
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
|
||||
{
|
||||
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
ChangeTracker.LazyLoadingEnabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All libraries of Kyoo. See <see cref="Library"/>.
|
||||
/// </summary>
|
||||
public DbSet<Library> Libraries { get; set; }
|
||||
/// <summary>
|
||||
/// All collections of Kyoo. See <see cref="Collection"/>.
|
||||
/// </summary>
|
||||
public DbSet<Collection> Collections { get; set; }
|
||||
/// <summary>
|
||||
/// All shows of Kyoo. See <see cref="Show"/>.
|
||||
/// </summary>
|
||||
public DbSet<Show> Shows { get; set; }
|
||||
/// <summary>
|
||||
/// All seasons of Kyoo. See <see cref="Season"/>.
|
||||
/// </summary>
|
||||
public DbSet<Season> Seasons { get; set; }
|
||||
/// <summary>
|
||||
/// All episodes of Kyoo. See <see cref="Episode"/>.
|
||||
/// </summary>
|
||||
public DbSet<Episode> Episodes { get; set; }
|
||||
/// <summary>
|
||||
/// All tracks of Kyoo. See <see cref="Track"/>.
|
||||
/// </summary>
|
||||
public DbSet<Track> Tracks { get; set; }
|
||||
/// <summary>
|
||||
/// All genres of Kyoo. See <see cref="Genres"/>.
|
||||
/// </summary>
|
||||
public DbSet<Genre> Genres { get; set; }
|
||||
/// <summary>
|
||||
/// All people of Kyoo. See <see cref="People"/>.
|
||||
/// </summary>
|
||||
public DbSet<People> People { get; set; }
|
||||
/// <summary>
|
||||
/// All studios of Kyoo. See <see cref="Studio"/>.
|
||||
/// </summary>
|
||||
public DbSet<Studio> Studios { get; set; }
|
||||
public DbSet<ProviderID> Providers { get; set; }
|
||||
/// <summary>
|
||||
/// All providers of Kyoo. See <see cref="Provider"/>.
|
||||
/// </summary>
|
||||
public DbSet<Provider> Providers { get; set; }
|
||||
/// <summary>
|
||||
/// All metadataIDs (ExternalIDs) of Kyoo. See <see cref="MetadataID"/>.
|
||||
/// </summary>
|
||||
public DbSet<MetadataID> MetadataIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// All people's role. See <see cref="PeopleRole"/>.
|
||||
/// </summary>
|
||||
public DbSet<PeopleRole> PeopleRoles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a generic link between two resource types.
|
||||
/// </summary>
|
||||
/// <remarks>Types are order dependant. You can't inverse the order. Please always put the owner first.</remarks>
|
||||
/// <typeparam name="T1">The first resource type of the relation. It is the owner of the second</typeparam>
|
||||
/// <typeparam name="T2">The second resource type of the relation. It is the contained resource.</typeparam>
|
||||
/// <returns>All links between the two types.</returns>
|
||||
public DbSet<Link<T1, T2>> Links<T1, T2>()
|
||||
where T1 : class, IResource
|
||||
where T2 : class, IResource
|
||||
@ -39,7 +83,10 @@ namespace Kyoo
|
||||
return Set<Link<T1, T2>>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A basic constructor that set default values (query tracker behaviors, mapping enums...)
|
||||
/// </summary>
|
||||
public DatabaseContext()
|
||||
{
|
||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<Status>();
|
||||
@ -50,6 +97,21 @@ namespace Kyoo
|
||||
ChangeTracker.LazyLoadingEnabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="DatabaseContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">Connection options to use (witch databse provider to use, connection strings...)</param>
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> options)
|
||||
: base(options)
|
||||
{
|
||||
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
ChangeTracker.LazyLoadingEnabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set database parameters to support every types of Kyoo.
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder">The database's model builder.</param>
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
@ -58,14 +120,6 @@ namespace Kyoo
|
||||
modelBuilder.HasPostgresEnum<ItemType>();
|
||||
modelBuilder.HasPostgresEnum<StreamType>();
|
||||
|
||||
modelBuilder.Entity<Library>()
|
||||
.Property(x => x.Paths)
|
||||
.HasColumnType("text[]");
|
||||
|
||||
modelBuilder.Entity<Show>()
|
||||
.Property(x => x.Aliases)
|
||||
.HasColumnType("text[]");
|
||||
|
||||
modelBuilder.Entity<Track>()
|
||||
.Property(t => t.IsDefault)
|
||||
.ValueGeneratedNever();
|
||||
@ -74,17 +128,17 @@ namespace Kyoo
|
||||
.Property(t => t.IsForced)
|
||||
.ValueGeneratedNever();
|
||||
|
||||
modelBuilder.Entity<ProviderID>()
|
||||
modelBuilder.Entity<Provider>()
|
||||
.HasMany(x => x.Libraries)
|
||||
.WithMany(x => x.Providers)
|
||||
.UsingEntity<Link<Library, ProviderID>>(
|
||||
.UsingEntity<Link<Library, Provider>>(
|
||||
y => y
|
||||
.HasOne(x => x.First)
|
||||
.WithMany(x => x.ProviderLinks),
|
||||
y => y
|
||||
.HasOne(x => x.Second)
|
||||
.WithMany(x => x.LibraryLinks),
|
||||
y => y.HasKey(Link<Library, ProviderID>.PrimaryKey));
|
||||
y => y.HasKey(Link<Library, Provider>.PrimaryKey));
|
||||
|
||||
modelBuilder.Entity<Collection>()
|
||||
.HasMany(x => x.Libraries)
|
||||
@ -160,7 +214,7 @@ namespace Kyoo
|
||||
modelBuilder.Entity<Genre>().Property(x => x.Slug).IsRequired();
|
||||
modelBuilder.Entity<Library>().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<Studio>().Property(x => x.Slug).IsRequired();
|
||||
|
||||
@ -182,7 +236,7 @@ namespace Kyoo
|
||||
modelBuilder.Entity<Studio>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<ProviderID>()
|
||||
modelBuilder.Entity<Provider>()
|
||||
.HasIndex(x => x.Slug)
|
||||
.IsUnique();
|
||||
modelBuilder.Entity<Season>()
|
||||
@ -196,6 +250,13 @@ namespace Kyoo
|
||||
.IsUnique();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a new or an in cache temporary object wih the same ID as the one given
|
||||
/// </summary>
|
||||
/// <param name="model">If a resource with the same ID is found in the database, it will be used.
|
||||
/// <see cref="model"/> will be used overwise</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <returns>A resource that is now tracked by this context.</returns>
|
||||
public T GetTemporaryObject<T>(T model)
|
||||
where T : class, IResource
|
||||
{
|
||||
@ -206,6 +267,11 @@ namespace Kyoo
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes that are applied to this context.
|
||||
/// </summary>
|
||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public override int SaveChanges()
|
||||
{
|
||||
try
|
||||
@ -221,6 +287,13 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes that are applied to this context.
|
||||
/// </summary>
|
||||
/// <param name="acceptAllChangesOnSuccess">Indicates whether AcceptAllChanges() is called after the changes
|
||||
/// have been sent successfully to the database.</param>
|
||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
||||
{
|
||||
try
|
||||
@ -236,6 +309,13 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes that are applied to this context.
|
||||
/// </summary>
|
||||
/// <param name="duplicateMessage">The message that will have the <see cref="DuplicatedItemException"/>
|
||||
/// (if a duplicate is found).</param>
|
||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public int SaveChanges(string duplicateMessage)
|
||||
{
|
||||
try
|
||||
@ -251,6 +331,14 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes that are applied to this context.
|
||||
/// </summary>
|
||||
/// <param name="acceptAllChangesOnSuccess">Indicates whether AcceptAllChanges() is called after the changes
|
||||
/// have been sent successfully to the database.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
|
||||
CancellationToken cancellationToken = new())
|
||||
{
|
||||
@ -267,6 +355,12 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes that are applied to this context.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new())
|
||||
{
|
||||
try
|
||||
@ -282,6 +376,14 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes that are applied to this context.
|
||||
/// </summary>
|
||||
/// <param name="duplicateMessage">The message that will have the <see cref="DuplicatedItemException"/>
|
||||
/// (if a duplicate is found).</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public async Task<int> SaveChangesAsync(string duplicateMessage,
|
||||
CancellationToken cancellationToken = new())
|
||||
{
|
||||
@ -298,6 +400,12 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes if no duplicates are found. If one is found, no change are saved but the current changes are no discarded.
|
||||
/// The current context will still hold those invalid changes.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||
/// <returns>The number of state entries written to the database or -1 if a duplicate exist.</returns>
|
||||
public async Task<int> SaveIfNoDuplicates(CancellationToken cancellationToken = new())
|
||||
{
|
||||
try
|
||||
@ -310,12 +418,31 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save items or retry with a custom method if a duplicate is found.
|
||||
/// </summary>
|
||||
/// <param name="obj">The item to save (other changes of this context will also be saved)</param>
|
||||
/// <param name="onFail">A function to run on fail, the <see cref="obj"/> param wil be mapped.
|
||||
/// The second parameter is the current retry number.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||
/// <typeparam name="T">The type of the item to save</typeparam>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public Task<T> SaveOrRetry<T>(T obj, Func<T, int, T> onFail, CancellationToken cancellationToken = new())
|
||||
{
|
||||
return SaveOrRetry(obj, onFail, 0, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<T> SaveOrRetry<T>(T obj,
|
||||
/// <summary>
|
||||
/// Save items or retry with a custom method if a duplicate is found.
|
||||
/// </summary>
|
||||
/// <param name="obj">The item to save (other changes of this context will also be saved)</param>
|
||||
/// <param name="onFail">A function to run on fail, the <see cref="obj"/> param wil be mapped.
|
||||
/// The second parameter is the current retry number.</param>
|
||||
/// <param name="recurse">The current retry number.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||
/// <typeparam name="T">The type of the item to save</typeparam>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
private async Task<T> SaveOrRetry<T>(T obj,
|
||||
Func<T, int, T> onFail,
|
||||
int recurse,
|
||||
CancellationToken cancellationToken = new())
|
||||
@ -337,11 +464,20 @@ namespace Kyoo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the exception is a duplicated exception.
|
||||
/// </summary>
|
||||
/// <remarks>WARNING: this only works for PostgreSQL</remarks>
|
||||
/// <param name="ex">The exception to check</param>
|
||||
/// <returns>True if the exception is a duplicate exception. False otherwise</returns>
|
||||
private static bool IsDuplicateException(Exception ex)
|
||||
{
|
||||
return ex.InnerException is PostgresException {SqlState: PostgresErrorCodes.UniqueViolation};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete every changes that are on this context.
|
||||
/// </summary>
|
||||
private void DiscardChanges()
|
||||
{
|
||||
foreach (EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged
|
||||
|
@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20210325184215_Initial")]
|
||||
[Migration("20210420221509_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
@ -179,7 +179,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
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")
|
||||
.HasColumnType("integer");
|
||||
@ -191,7 +191,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
|
||||
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 =>
|
||||
@ -320,7 +320,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("PeopleRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Provider", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -563,7 +563,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
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")
|
||||
.WithMany("ProviderLinks")
|
||||
@ -571,7 +571,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Second")
|
||||
b.HasOne("Kyoo.Models.Provider", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@ -632,7 +632,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.HasForeignKey("PeopleID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Provider")
|
||||
b.HasOne("Kyoo.Models.Provider", "Provider")
|
||||
.WithMany("MetadataLinks")
|
||||
.HasForeignKey("ProviderID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@ -744,7 +744,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Navigation("Roles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Provider", b =>
|
||||
{
|
||||
b.Navigation("LibraryLinks");
|
||||
|
@ -128,7 +128,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Link<Library, ProviderID>",
|
||||
name: "Link<Library, Provider>",
|
||||
columns: table => new
|
||||
{
|
||||
FirstID = table.Column<int>(type: "integer", nullable: false),
|
||||
@ -136,15 +136,15 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
},
|
||||
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(
|
||||
name: "FK_Link<Library, ProviderID>_Libraries_FirstID",
|
||||
name: "FK_Link<Library, Provider>_Libraries_FirstID",
|
||||
column: x => x.FirstID,
|
||||
principalTable: "Libraries",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Link<Library, ProviderID>_Providers_SecondID",
|
||||
name: "FK_Link<Library, Provider>_Providers_SecondID",
|
||||
column: x => x.SecondID,
|
||||
principalTable: "Providers",
|
||||
principalColumn: "ID",
|
||||
@ -459,8 +459,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
column: "SecondID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Link<Library, ProviderID>_SecondID",
|
||||
table: "Link<Library, ProviderID>",
|
||||
name: "IX_Link<Library, Provider>_SecondID",
|
||||
table: "Link<Library, Provider>",
|
||||
column: "SecondID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
@ -559,7 +559,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
name: "Link<Library, Collection>");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Link<Library, ProviderID>");
|
||||
name: "Link<Library, Provider>");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Link<Library, Show>");
|
@ -177,7 +177,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
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")
|
||||
.HasColumnType("integer");
|
||||
@ -189,7 +189,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
|
||||
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 =>
|
||||
@ -318,7 +318,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.ToTable("PeopleRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Provider", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -561,7 +561,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
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")
|
||||
.WithMany("ProviderLinks")
|
||||
@ -569,7 +569,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Second")
|
||||
b.HasOne("Kyoo.Models.Provider", "Second")
|
||||
.WithMany("LibraryLinks")
|
||||
.HasForeignKey("SecondID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@ -630,7 +630,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
.HasForeignKey("PeopleID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Kyoo.Models.ProviderID", "Provider")
|
||||
b.HasOne("Kyoo.Models.Provider", "Provider")
|
||||
.WithMany("MetadataLinks")
|
||||
.HasForeignKey("ProviderID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@ -742,7 +742,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||
b.Navigation("Roles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Kyoo.Models.ProviderID", b =>
|
||||
modelBuilder.Entity("Kyoo.Models.Provider", b =>
|
||||
{
|
||||
b.Navigation("LibraryLinks");
|
||||
|
||||
|
@ -3,7 +3,11 @@ using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.VisualBasic.FileIO;
|
||||
using Microsoft.AspNetCore.Hosting.StaticWebAssets;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
@ -18,12 +22,9 @@ namespace Kyoo
|
||||
/// <param name="args">Command line arguments</param>
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
FileSystem.CurrentDirectory = args[0];
|
||||
if (!File.Exists("./appsettings.json"))
|
||||
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json"), "appsettings.json");
|
||||
|
||||
|
||||
if (!File.Exists("./settings.json"))
|
||||
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "settings.json"), "settings.json");
|
||||
|
||||
bool? debug = Environment.GetEnvironmentVariable("ENVIRONMENT")?.ToLowerInvariant() switch
|
||||
{
|
||||
"d" => true,
|
||||
@ -49,15 +50,50 @@ namespace Kyoo
|
||||
await host.Build().RunAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register settings.json, environment variables and command lines arguments as configuration.
|
||||
/// </summary>
|
||||
/// <param name="builder">The configuration builder to use</param>
|
||||
/// <param name="args">The command line arguments</param>
|
||||
/// <returns>The modified configuration builder</returns>
|
||||
private static IConfigurationBuilder SetupConfig(IConfigurationBuilder builder, string[] args)
|
||||
{
|
||||
return builder.AddJsonFile("./settings.json", false, true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Createa a web host
|
||||
/// </summary>
|
||||
/// <param name="args">Command line parameters that can be handled by kestrel</param>
|
||||
/// <returns>A new web host instance</returns>
|
||||
private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseKestrel(config => { config.AddServerHeader = false; })
|
||||
.UseUrls("http://*:5000")
|
||||
private static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
WebHost.CreateDefaultBuilder(args);
|
||||
|
||||
return new WebHostBuilder()
|
||||
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
|
||||
.UseConfiguration(SetupConfig(new ConfigurationBuilder(), args).Build())
|
||||
.ConfigureAppConfiguration(x => SetupConfig(x, args))
|
||||
.ConfigureLogging((context, builder) =>
|
||||
{
|
||||
builder.AddConfiguration(context.Configuration.GetSection("logging"))
|
||||
.AddConsole()
|
||||
.AddDebug()
|
||||
.AddEventSourceLogger();
|
||||
})
|
||||
.UseDefaultServiceProvider((context, options) =>
|
||||
{
|
||||
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
|
||||
if (context.HostingEnvironment.IsDevelopment())
|
||||
StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration);
|
||||
})
|
||||
.ConfigureServices(x => x.AddRouting())
|
||||
.UseKestrel(options => { options.AddServerHeader = false; })
|
||||
.UseIIS()
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<ILibraryItemRepository, LibraryItemRepository>();
|
||||
services.AddScoped<ICollectionRepository, CollectionRepository>();
|
||||
|
@ -32,8 +32,8 @@ namespace Kyoo.Controllers
|
||||
public async Task<IEnumerable<string>> GetPossibleParameters()
|
||||
{
|
||||
using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
return (await libraryManager!.GetLibraries()).Select(x => x.Slug);
|
||||
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
return (await libraryManager!.GetAll<Library>()).Select(x => x.Slug);
|
||||
}
|
||||
|
||||
public int? Progress()
|
||||
@ -56,25 +56,25 @@ namespace Kyoo.Controllers
|
||||
_parallelTasks = 30;
|
||||
|
||||
using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
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))
|
||||
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)
|
||||
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)
|
||||
if (!File.Exists(track.Path))
|
||||
await libraryManager.DeleteTrack(track);
|
||||
await libraryManager.Delete(track);
|
||||
|
||||
ICollection<Library> libraries = argument == null
|
||||
? await libraryManager.GetLibraries()
|
||||
: new [] { await libraryManager.GetLibrary(argument)};
|
||||
? await libraryManager.GetAll<Library>()
|
||||
: new [] { await libraryManager.Get<Library>(argument)};
|
||||
|
||||
if (argument != null && libraries.First() == null)
|
||||
throw new ArgumentException($"No library found with the name {argument}");
|
||||
@ -144,44 +144,48 @@ namespace Kyoo.Controllers
|
||||
|
||||
private async Task RegisterExternalSubtitle(string path, CancellationToken token)
|
||||
{
|
||||
if (token.IsCancellationRequested || path.Split(Path.DirectorySeparatorChar).Contains("Subtitles"))
|
||||
return;
|
||||
using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
|
||||
string patern = _config.GetValue<string>("subtitleRegex");
|
||||
Regex regex = new(patern, RegexOptions.IgnoreCase);
|
||||
Match match = regex.Match(path);
|
||||
|
||||
if (!match.Success)
|
||||
try
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"The subtitle at {path} does not match the subtitle's regex.");
|
||||
return;
|
||||
if (token.IsCancellationRequested || path.Split(Path.DirectorySeparatorChar).Contains("Subtitles"))
|
||||
return;
|
||||
using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
|
||||
string patern = _config.GetValue<string>("subtitleRegex");
|
||||
Regex regex = new(patern, RegexOptions.IgnoreCase);
|
||||
Match match = regex.Match(path);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"The subtitle at {path} does not match the subtitle's regex.");
|
||||
return;
|
||||
}
|
||||
|
||||
string episodePath = match.Groups["Episode"].Value;
|
||||
Episode episode = await libraryManager!.Get<Episode>(x => x.Path.StartsWith(episodePath));
|
||||
Track track = new()
|
||||
{
|
||||
Type = StreamType.Subtitle,
|
||||
Language = match.Groups["Language"].Value,
|
||||
IsDefault = match.Groups["Default"].Value.Length > 0,
|
||||
IsForced = match.Groups["Forced"].Value.Length > 0,
|
||||
Codec = SubtitleExtensions[Path.GetExtension(path)],
|
||||
IsExternal = true,
|
||||
Path = path,
|
||||
Episode = episode
|
||||
};
|
||||
|
||||
await libraryManager.Create(track);
|
||||
Console.WriteLine($"Registering subtitle at: {path}.");
|
||||
}
|
||||
|
||||
string episodePath = match.Groups["Episode"].Value;
|
||||
Episode episode = await libraryManager!.GetEpisode(x => x.Path.StartsWith(episodePath));
|
||||
|
||||
if (episode == null)
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"No episode found for subtitle at: ${path}.");
|
||||
return;
|
||||
}
|
||||
|
||||
Track track = new(StreamType.Subtitle,
|
||||
null,
|
||||
match.Groups["Language"].Value,
|
||||
match.Groups["Default"].Value.Length > 0,
|
||||
match.Groups["Forced"].Value.Length > 0,
|
||||
SubtitleExtensions[Path.GetExtension(path)],
|
||||
true,
|
||||
path)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Episode = episode
|
||||
};
|
||||
|
||||
await libraryManager.RegisterTrack(track);
|
||||
Console.WriteLine($"Registering subtitle at: {path}.");
|
||||
await Console.Error.WriteLineAsync($"Unknown error while registering subtitle: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RegisterFile(string path, string relativePath, Library library, CancellationToken token)
|
||||
@ -192,7 +196,7 @@ namespace Kyoo.Controllers
|
||||
try
|
||||
{
|
||||
using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
||||
await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
||||
|
||||
string patern = _config.GetValue<string>("regex");
|
||||
Regex regex = new(patern, RegexOptions.IgnoreCase);
|
||||
@ -215,7 +219,7 @@ namespace Kyoo.Controllers
|
||||
bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1;
|
||||
Show show = await GetShow(libraryManager, showName, showPath, isMovie, library);
|
||||
if (isMovie)
|
||||
await libraryManager!.RegisterEpisode(await GetMovie(show, path));
|
||||
await libraryManager!.Create(await GetMovie(show, path));
|
||||
else
|
||||
{
|
||||
Season season = await GetSeason(libraryManager, show, seasonNumber, library);
|
||||
@ -226,7 +230,7 @@ namespace Kyoo.Controllers
|
||||
absoluteNumber,
|
||||
path,
|
||||
library);
|
||||
await libraryManager!.RegisterEpisode(episode);
|
||||
await libraryManager!.Create(episode);
|
||||
}
|
||||
|
||||
await libraryManager.AddShowLink(show, library, collection);
|
||||
@ -250,19 +254,19 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
if (string.IsNullOrEmpty(collectionName))
|
||||
return null;
|
||||
Collection collection = await libraryManager.GetCollection(Utility.ToSlug(collectionName));
|
||||
Collection collection = await libraryManager.Get<Collection>(Utility.ToSlug(collectionName));
|
||||
if (collection != null)
|
||||
return collection;
|
||||
collection = await _metadataProvider.GetCollectionFromName(collectionName, library);
|
||||
|
||||
try
|
||||
{
|
||||
await libraryManager.RegisterCollection(collection);
|
||||
await libraryManager.Create(collection);
|
||||
return collection;
|
||||
}
|
||||
catch (DuplicatedItemException)
|
||||
{
|
||||
return await libraryManager.GetCollection(collection.Slug);
|
||||
return await libraryManager.Get<Collection>(collection.Slug);
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +276,7 @@ namespace Kyoo.Controllers
|
||||
bool isMovie,
|
||||
Library library)
|
||||
{
|
||||
Show old = await libraryManager.GetShow(x => x.Path == showPath);
|
||||
Show old = await libraryManager.Get<Show>(x => x.Path == showPath);
|
||||
if (old != null)
|
||||
{
|
||||
await libraryManager.Load(old, x => x.ExternalIDs);
|
||||
@ -284,18 +288,18 @@ namespace Kyoo.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
show = await libraryManager.RegisterShow(show);
|
||||
show = await libraryManager.Create(show);
|
||||
}
|
||||
catch (DuplicatedItemException)
|
||||
{
|
||||
old = await libraryManager.GetShow(show.Slug);
|
||||
old = await libraryManager.Get<Show>(show.Slug);
|
||||
if (old.Path == showPath)
|
||||
{
|
||||
await libraryManager.Load(old, x => x.ExternalIDs);
|
||||
return old;
|
||||
}
|
||||
show.Slug += $"-{show.StartYear}";
|
||||
await libraryManager.RegisterShow(show);
|
||||
await libraryManager.Create(show);
|
||||
}
|
||||
await _thumbnailsManager.Validate(show);
|
||||
return show;
|
||||
@ -308,22 +312,20 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
if (seasonNumber == -1)
|
||||
return default;
|
||||
Season season = await libraryManager.GetSeason(show.Slug, seasonNumber);
|
||||
if (season == null)
|
||||
try
|
||||
{
|
||||
season = await _metadataProvider.GetSeason(show, seasonNumber, library);
|
||||
try
|
||||
{
|
||||
await libraryManager.RegisterSeason(season);
|
||||
await _thumbnailsManager.Validate(season);
|
||||
}
|
||||
catch (DuplicatedItemException)
|
||||
{
|
||||
season = await libraryManager.GetSeason(show.Slug, season.SeasonNumber);
|
||||
}
|
||||
Season season = await libraryManager.Get(show.Slug, seasonNumber);
|
||||
season.Show = show;
|
||||
return season;
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
Season season = await _metadataProvider.GetSeason(show, seasonNumber, library);
|
||||
await libraryManager.CreateIfNotExists(season);
|
||||
await _thumbnailsManager.Validate(season);
|
||||
season.Show = show;
|
||||
return season;
|
||||
}
|
||||
season.Show = show;
|
||||
return season;
|
||||
}
|
||||
|
||||
private async Task<Episode> GetEpisode(ILibraryManager libraryManager,
|
||||
|
@ -45,27 +45,25 @@ namespace Kyoo.Tasks
|
||||
case "show":
|
||||
case "shows":
|
||||
Show show = await (int.TryParse(slug, out id)
|
||||
? _library!.GetShow(id)
|
||||
: _library!.GetShow(slug));
|
||||
? _library!.Get<Show>(id)
|
||||
: _library!.Get<Show>(slug));
|
||||
await ExtractShow(show, thumbs, subs, token);
|
||||
break;
|
||||
case "season":
|
||||
case "seasons":
|
||||
Season season = await (int.TryParse(slug, out id)
|
||||
? _library!.GetSeason(id)
|
||||
: _library!.GetSeason(slug));
|
||||
? _library!.Get<Season>(id)
|
||||
: _library!.Get<Season>(slug));
|
||||
await ExtractSeason(season, thumbs, subs, token);
|
||||
break;
|
||||
case "episode":
|
||||
case "episodes":
|
||||
Episode episode = await (int.TryParse(slug, out id)
|
||||
? _library!.GetEpisode(id)
|
||||
: _library!.GetEpisode(slug));
|
||||
? _library!.Get<Episode>(id)
|
||||
: _library!.Get<Episode>(slug));
|
||||
await ExtractEpisode(episode, thumbs, subs);
|
||||
break;
|
||||
}
|
||||
|
||||
await _library!.DisposeAsync();
|
||||
}
|
||||
|
||||
private async Task ExtractShow(Show show, bool thumbs, bool subs, CancellationToken token)
|
||||
@ -105,7 +103,7 @@ namespace Kyoo.Tasks
|
||||
.Where(x => x.Type != StreamType.Attachment)
|
||||
.Concat(episode.Tracks.Where(x => x.IsExternal))
|
||||
.ToList();
|
||||
await _library.EditEpisode(episode, false);
|
||||
await _library.Edit(episode, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetCollection(id) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Collection>(id) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -61,12 +61,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetCollection(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Collection>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -87,12 +87,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Library>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetCollection(id) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Collection>(id) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -113,12 +113,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Library>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetCollection(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Collection>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.CommonApi;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
@ -35,42 +36,56 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy = "Read")]
|
||||
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")]
|
||||
[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")]
|
||||
[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")]
|
||||
[Authorize(Policy = "Read")]
|
||||
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")]
|
||||
[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);
|
||||
try
|
||||
{
|
||||
return await _libraryManager.Get(showSlug, seasonNumber);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
|
||||
[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);
|
||||
try
|
||||
{
|
||||
return await _libraryManager.Get(showID, seasonNumber);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{episodeID:int}/track")]
|
||||
@ -84,12 +99,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Track> resources = await _libraryManager.GetTracks(
|
||||
ICollection<Track> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Track>(where, x => x.Episode.ID == episodeID),
|
||||
new Sort<Track>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetEpisode(episodeID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Episode>(episodeID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -112,14 +127,14 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Track> resources = await _libraryManager.GetTracks(
|
||||
ICollection<Track> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Track>(where, x => x.Episode.ShowID == showID
|
||||
&& x.Episode.SeasonNumber == seasonNumber
|
||||
&& x.Episode.EpisodeNumber == episodeNumber),
|
||||
new Sort<Track>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetEpisode(showID, seasonNumber, episodeNumber) == null)
|
||||
if (!resources.Any() && await _libraryManager.GetOrDefault(showID, seasonNumber, episodeNumber) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -129,10 +144,10 @@ namespace Kyoo.Api
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/track")]
|
||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
|
||||
[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/track")]
|
||||
[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<ActionResult<Page<Track>>> GetEpisode(string showSlug,
|
||||
public async Task<ActionResult<Page<Track>>> GetEpisode(string slug,
|
||||
int seasonNumber,
|
||||
int episodeNumber,
|
||||
[FromQuery] string sortBy,
|
||||
@ -142,13 +157,14 @@ namespace Kyoo.Api
|
||||
{
|
||||
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 == slug
|
||||
&& x.Episode.SeasonNumber == seasonNumber
|
||||
&& x.Episode.EpisodeNumber == episodeNumber),
|
||||
new Sort<Track>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber) == null)
|
||||
if (!resources.Any() && await _libraryManager.GetOrDefault(slug, seasonNumber, episodeNumber) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -162,20 +178,30 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetThumb(int id)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetEpisode(id);
|
||||
if (episode == null)
|
||||
try
|
||||
{
|
||||
Episode episode = await _libraryManager.Get<Episode>(id);
|
||||
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/thumb")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetThumb(string slug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetEpisode(slug);
|
||||
if (episode == null)
|
||||
try
|
||||
{
|
||||
Episode episode = await _libraryManager.Get<Episode>(slug);
|
||||
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -36,12 +36,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetGenre(id) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Genre>(id) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -62,12 +62,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetGenre(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Genre>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
|
@ -47,12 +47,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetLibrary(id) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Library>(id) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -73,12 +73,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetLibrary(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -99,12 +99,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Collection>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetLibrary(id) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Library>(id) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -125,12 +125,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Collection>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetLibrary(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -156,7 +156,7 @@ namespace Kyoo.Api
|
||||
new Sort<LibraryItem>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetLibrary(id) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Library>(id) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -182,7 +182,7 @@ namespace Kyoo.Api
|
||||
new Sort<LibraryItem>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetLibrary(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy="Read")]
|
||||
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));
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy="Read")]
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace Kyoo.Api
|
||||
[Route("api/provider")]
|
||||
[Route("api/providers")]
|
||||
[ApiController]
|
||||
public class ProviderAPI : CrudApi<ProviderID>
|
||||
public class ProviderAPI : CrudApi<Provider>
|
||||
{
|
||||
private readonly IThumbnailsManager _thumbnails;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
@ -32,7 +32,7 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy="Read")]
|
||||
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));
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy="Read")]
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Kyoo.Api
|
||||
{
|
||||
[Route("api/search")]
|
||||
[Route("api/search/{query}")]
|
||||
[ApiController]
|
||||
public class SearchApi : ControllerBase
|
||||
{
|
||||
@ -18,67 +18,67 @@ namespace Kyoo.Api
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
[HttpGet("{query}")]
|
||||
[HttpGet]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<ActionResult<SearchResult>> Search(string query)
|
||||
{
|
||||
return new SearchResult
|
||||
{
|
||||
Query = query,
|
||||
Collections = await _libraryManager.SearchCollections(query),
|
||||
Shows = await _libraryManager.SearchShows(query),
|
||||
Episodes = await _libraryManager.SearchEpisodes(query),
|
||||
People = await _libraryManager.SearchPeople(query),
|
||||
Genres = await _libraryManager.SearchGenres(query),
|
||||
Studios = await _libraryManager.SearchStudios(query)
|
||||
Collections = await _libraryManager.Search<Collection>(query),
|
||||
Shows = await _libraryManager.Search<Show>(query),
|
||||
Episodes = await _libraryManager.Search<Episode>(query),
|
||||
People = await _libraryManager.Search<People>(query),
|
||||
Genres = await _libraryManager.Search<Genre>(query),
|
||||
Studios = await _libraryManager.Search<Studio>(query)
|
||||
};
|
||||
}
|
||||
|
||||
[HttpGet("{query}/collection")]
|
||||
[HttpGet("{query}/collections")]
|
||||
[HttpGet("collection")]
|
||||
[HttpGet("collections")]
|
||||
[Authorize(Policy="Read")]
|
||||
public Task<ICollection<Collection>> SearchCollections(string query)
|
||||
{
|
||||
return _libraryManager.SearchCollections(query);
|
||||
return _libraryManager.Search<Collection>(query);
|
||||
}
|
||||
|
||||
[HttpGet("{query}/show")]
|
||||
[HttpGet("{query}/shows")]
|
||||
[HttpGet("show")]
|
||||
[HttpGet("shows")]
|
||||
[Authorize(Policy="Read")]
|
||||
public Task<ICollection<Show>> SearchShows(string query)
|
||||
{
|
||||
return _libraryManager.SearchShows(query);
|
||||
return _libraryManager.Search<Show>(query);
|
||||
}
|
||||
|
||||
[HttpGet("{query}/episode")]
|
||||
[HttpGet("{query}/episodes")]
|
||||
[HttpGet("episode")]
|
||||
[HttpGet("episodes")]
|
||||
[Authorize(Policy="Read")]
|
||||
public Task<ICollection<Episode>> SearchEpisodes(string query)
|
||||
{
|
||||
return _libraryManager.SearchEpisodes(query);
|
||||
return _libraryManager.Search<Episode>(query);
|
||||
}
|
||||
|
||||
[HttpGet("{query}/people")]
|
||||
[HttpGet("people")]
|
||||
[Authorize(Policy="Read")]
|
||||
public Task<ICollection<People>> SearchPeople(string query)
|
||||
{
|
||||
return _libraryManager.SearchPeople(query);
|
||||
return _libraryManager.Search<People>(query);
|
||||
}
|
||||
|
||||
[HttpGet("{query}/genre")]
|
||||
[HttpGet("{query}/genres")]
|
||||
[HttpGet("genre")]
|
||||
[HttpGet("genres")]
|
||||
[Authorize(Policy="Read")]
|
||||
public Task<ICollection<Genre>> SearchGenres(string query)
|
||||
{
|
||||
return _libraryManager.SearchGenres(query);
|
||||
return _libraryManager.Search<Genre>(query);
|
||||
}
|
||||
|
||||
[HttpGet("{query}/studio")]
|
||||
[HttpGet("{query}/studios")]
|
||||
[HttpGet("studio")]
|
||||
[HttpGet("studios")]
|
||||
[Authorize(Policy="Read")]
|
||||
public Task<ICollection<Studio>> SearchStudios(string query)
|
||||
{
|
||||
return _libraryManager.SearchStudios(query);
|
||||
return _libraryManager.Search<Studio>(query);
|
||||
}
|
||||
}
|
||||
}
|
@ -42,12 +42,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Episode>(where, x => x.SeasonID == seasonID),
|
||||
new Sort<Episode>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetSeason(seasonID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Season>(seasonID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -69,13 +69,13 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Episode>(where, x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber),
|
||||
new Sort<Episode>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetSeason(showSlug, seasonNumber) == null)
|
||||
if (!resources.Any() && await _libraryManager.GetOrDefault(showSlug, seasonNumber) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -97,12 +97,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Episode>(where, x => x.ShowID == showID && x.SeasonNumber == seasonNumber),
|
||||
new Sort<Episode>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetSeason(showID, seasonNumber) == null)
|
||||
if (!resources.Any() && await _libraryManager.GetOrDefault(showID, seasonNumber) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -116,28 +116,28 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy = "Read")]
|
||||
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")]
|
||||
[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")]
|
||||
[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")]
|
||||
[Authorize(Policy="Read")]
|
||||
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);
|
||||
return _files.FileResult(await _thumbs.GetSeasonPoster(season));
|
||||
}
|
||||
@ -146,7 +146,7 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy="Read")]
|
||||
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);
|
||||
return _files.FileResult(await _thumbs.GetSeasonPoster(season));
|
||||
}
|
||||
|
@ -44,12 +44,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Season> resources = await _libraryManager.GetSeasons(
|
||||
ICollection<Season> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Season>(where, x => x.ShowID == showID),
|
||||
new Sort<Season>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(showID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -70,12 +70,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Season> resources = await _libraryManager.GetSeasons(
|
||||
ICollection<Season> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Season>(where, x => x.Show.Slug == slug),
|
||||
new Sort<Season>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -96,12 +96,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Episode>(where, x => x.ShowID == showID),
|
||||
new Sort<Episode>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(showID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -122,12 +122,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetEpisodes(
|
||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Episode>(where, x => x.Show.Slug == slug),
|
||||
new Sort<Episode>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -152,7 +152,7 @@ namespace Kyoo.Api
|
||||
new Sort<PeopleRole>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(showID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -177,7 +177,7 @@ namespace Kyoo.Api
|
||||
new Sort<PeopleRole>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -198,12 +198,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Genre>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(showID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -224,12 +224,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Genre>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -245,7 +245,7 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -259,7 +259,7 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -278,12 +278,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Library>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(showID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -304,12 +304,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Library>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -330,12 +330,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Collection>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(showID) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(showID) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -356,12 +356,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)),
|
||||
new Sort<Collection>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetShow(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Show>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -376,13 +376,18 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<ActionResult<Dictionary<string, string>>> GetFonts(string slug)
|
||||
{
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
try
|
||||
{
|
||||
Show show = await _libraryManager.Get<Show>(slug);
|
||||
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments");
|
||||
return (await _files.ListFiles(path))
|
||||
.ToDictionary(Path.GetFileNameWithoutExtension,
|
||||
x => $"{BaseURL}/api/shows/{slug}/fonts/{Path.GetFileName(x)}");
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments");
|
||||
return (await _files.ListFiles(path))
|
||||
.ToDictionary(Path.GetFileNameWithoutExtension,
|
||||
x => $"{BaseURL}/api/shows/{slug}/fonts/{Path.GetFileName(x)}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{showSlug}/font/{slug}")]
|
||||
@ -390,41 +395,61 @@ namespace Kyoo.Api
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<IActionResult> GetFont(string showSlug, string slug)
|
||||
{
|
||||
Show show = await _libraryManager.GetShow(showSlug);
|
||||
if (show == null)
|
||||
try
|
||||
{
|
||||
Show show = await _libraryManager.Get<Show>(showSlug);
|
||||
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments", slug);
|
||||
return _files.FileResult(path);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments", slug);
|
||||
return _files.FileResult(path);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/poster")]
|
||||
[Authorize(Policy = "Read")]
|
||||
public async Task<IActionResult> GetPoster(string slug)
|
||||
{
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
try
|
||||
{
|
||||
Show show = await _libraryManager.Get<Show>(slug);
|
||||
return _files.FileResult(await _thumbs.GetShowPoster(show));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbs.GetShowPoster(show));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/logo")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetLogo(string slug)
|
||||
{
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
try
|
||||
{
|
||||
Show show = await _libraryManager.Get<Show>(slug);
|
||||
return _files.FileResult(await _thumbs.GetShowLogo(show));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbs.GetShowLogo(show));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{slug}/backdrop")]
|
||||
[Authorize(Policy="Read")]
|
||||
public async Task<IActionResult> GetBackdrop(string slug)
|
||||
{
|
||||
Show show = await _libraryManager.GetShow(slug);
|
||||
if (show == null)
|
||||
try
|
||||
{
|
||||
Show show = await _libraryManager.Get<Show>(slug);
|
||||
return _files.FileResult(await _thumbs.GetShowBackdrop(show));
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
return _files.FileResult(await _thumbs.GetShowBackdrop(show));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,12 +35,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
ICollection<Show> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Show>(where, x => x.StudioID == id),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetStudio(id) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Studio>(id) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
@ -61,12 +61,12 @@ namespace Kyoo.Api
|
||||
{
|
||||
try
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetShows(
|
||||
ICollection<Show> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere<Show>(where, x => x.Studio.Slug == slug),
|
||||
new Sort<Show>(sortBy),
|
||||
new Pagination(limit, afterID));
|
||||
|
||||
if (!resources.Any() && await _libraryManager.GetStudio(slug) == null)
|
||||
if (!resources.Any() && await _libraryManager.Get<Studio>(slug) == null)
|
||||
return NotFound();
|
||||
return Page(resources, limit);
|
||||
}
|
||||
|
@ -30,14 +30,14 @@ namespace Kyoo.Api
|
||||
Track subtitle;
|
||||
try
|
||||
{
|
||||
subtitle = await _libraryManager.GetTrack(slug, StreamType.Subtitle);
|
||||
subtitle = await _libraryManager.GetOrDefault(slug, StreamType.Subtitle);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
return BadRequest(new {error = ex.Message});
|
||||
}
|
||||
|
||||
if (subtitle == null || subtitle.Type != StreamType.Subtitle)
|
||||
if (subtitle is not {Type: StreamType.Subtitle})
|
||||
return NotFound();
|
||||
|
||||
if (subtitle.Codec == "subrip" && extension == "vtt")
|
||||
|
@ -29,7 +29,7 @@ namespace Kyoo.Api
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -45,7 +45,7 @@ namespace Kyoo.Api
|
||||
{
|
||||
// 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
|
||||
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)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@ using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
@ -40,92 +41,59 @@ namespace Kyoo.Api
|
||||
ctx.HttpContext.Response.Headers.Add("Expires", "0");
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
||||
[HttpGet("direct/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> DirectEpisode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
if (seasonNumber < 0 || episodeNumber < 0)
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
return _files.FileResult(episode.Path, true);
|
||||
}
|
||||
|
||||
[HttpGet("{movieSlug}")]
|
||||
[HttpGet("direct/{movieSlug}")]
|
||||
[HttpGet("{slug}")]
|
||||
[HttpGet("direct/{slug}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> DirectMovie(string movieSlug)
|
||||
public async Task<IActionResult> Direct(string slug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode == null)
|
||||
try
|
||||
{
|
||||
Episode episode = await _libraryManager.Get<Episode>(slug);
|
||||
return _files.FileResult(episode.Path, true);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
return _files.FileResult(episode.Path, true);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("transmux/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/master.m3u8")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> TransmuxEpisode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
if (seasonNumber < 0 || episodeNumber < 0)
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transmux(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
|
||||
[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();
|
||||
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("transmux/{slug}/master.m3u8")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> TranscodeEpisode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
public async Task<IActionResult> Transmux(string slug)
|
||||
{
|
||||
if (seasonNumber < 0 || episodeNumber < 0)
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{movieSlug}/master.m3u8")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> TranscodeMovie(string movieSlug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
try
|
||||
{
|
||||
Episode episode = await _libraryManager.Get<Episode>(slug);
|
||||
string path = await _transcoder.Transmux(episode);
|
||||
|
||||
if (episode == null)
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{slug}/master.m3u8")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> Transcode(string slug)
|
||||
{
|
||||
try
|
||||
{
|
||||
Episode episode = await _libraryManager.Get<Episode>(slug);
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return _files.FileResult(path, true);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@ -17,24 +18,19 @@ namespace Kyoo.Api
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
||||
[HttpGet("{slug}")]
|
||||
[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);
|
||||
if (item == null)
|
||||
try
|
||||
{
|
||||
Episode item = await _libraryManager.Get<Episode>(slug);
|
||||
return await WatchItem.FromEpisode(item, _libraryManager);
|
||||
}
|
||||
catch (ItemNotFound)
|
||||
{
|
||||
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)
|
||||
return NotFound();
|
||||
return await WatchItem.FromEpisode(item, _libraryManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,25 @@
|
||||
{
|
||||
"server.urls": "http://0.0.0.0:5000",
|
||||
"server.urls": "http://*:5000",
|
||||
"public_url": "http://localhost:5000/",
|
||||
"http_port": 5000,
|
||||
"https_port": 44300,
|
||||
|
||||
"Database": {
|
||||
"Server": "127.0.0.1",
|
||||
"Port": "5432",
|
||||
"Database": "kyooDB",
|
||||
"User Id": "kyoo",
|
||||
"Password": "kyooPassword",
|
||||
"Pooling": "true",
|
||||
"MaxPoolSize": "95",
|
||||
"Timeout": "30"
|
||||
"database": {
|
||||
"server": "127.0.0.1",
|
||||
"port": "5432",
|
||||
"database": "kyooDB",
|
||||
"user ID": "kyoo",
|
||||
"password": "kyooPassword",
|
||||
"pooling": "true",
|
||||
"maxPoolSize": "95",
|
||||
"timeout": "30"
|
||||
},
|
||||
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"logging": {
|
||||
"logLevel": {
|
||||
"default": "Warning",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information",
|
||||
"Microsoft.EntityFrameworkCore.DbUpdateException": "None",
|
||||
"Microsoft.EntityFrameworkCore.Update": "None",
|
||||
"Microsoft.EntityFrameworkCore.Database.Command": "None"
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
|
||||
"parallelTasks": "1",
|
||||
|
@ -5,7 +5,8 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
User=kyoo
|
||||
ExecStart=/usr/lib/kyoo/Kyoo /var/lib/kyoo
|
||||
WorkingDirectory=/var/lib/kyoo
|
||||
ExecStart=/usr/lib/kyoo/Kyoo
|
||||
Restart=on-abort
|
||||
TimeoutSec=20
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user