diff --git a/Kyoo.Common/Controllers/ILibraryManager.cs b/Kyoo.Common/Controllers/ILibraryManager.cs index 8268cb66..c345bf38 100644 --- a/Kyoo.Common/Controllers/ILibraryManager.cs +++ b/Kyoo.Common/Controllers/ILibraryManager.cs @@ -1,58 +1,93 @@ -using Kyoo.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using JetBrains.Annotations; +using Kyoo.Models; namespace Kyoo.Controllers { public interface ILibraryManager { // Get by slug - Library GetLibrary(string librarySlug); - Collection GetCollection(string slug); - Show GetShow(string slug); - Season GetSeason(string showSlug, long seasonNumber); - Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber); - Episode GetMovieEpisode(string movieSlug); - Genre GetGenre(string slug); - Studio GetStudio(string slug); - People GetPeople(string slug); - ProviderID GetProvider(string name); + Task GetLibrary(string slug); + Task GetCollection(string slug); + Task GetShow(string slug); + Task GetSeason(string showSlug, int seasonNumber); + Task GetEpisode(string showSlug, int seasonNumber, int episodeNumber); + Task GetMovieEpisode(string movieSlug); + Task GetTrack(int id); + Task GetTrack(int episodeID, string language, bool isForced); + Task GetGenre(string slug); + Task GetStudio(string slug); + Task GetPeople(string slug); + // Get by relations + Task> GetSeasons(int showID); + Task> GetSeasons(string showSlug); + + Task> GetEpisodes(int showID, int seasonNumber); + Task> GetEpisodes(string showSlug, int seasonNumber); + Task> GetEpisodes(int seasonID); + + + // Helpers + Task GetShowByPath(string path); + Task AddShowLink(int showID, int? libraryID, int? collectionID); + Task AddShowLink([NotNull] Show show, Library library, Collection collection); + // Get all - IEnumerable GetLibraries(); - IEnumerable GetCollections(); - IEnumerable GetShows(); - IEnumerable GetEpisodes(); - IEnumerable GetTracks(); - IEnumerable GetStudios(); - IEnumerable GetPeoples(); - IEnumerable GetGenres(); + Task> GetLibraries(); + Task> GetCollections(); + Task> GetShows(); + Task> GetSeasons(); + Task> GetEpisodes(); + Task> GetTracks(); + Task> GetStudios(); + Task> GetPeoples(); + Task> GetGenres(); + Task> GetProviders(); // Search - IEnumerable SearchCollections(string searchQuery); - IEnumerable SearchShows(string searchQuery); - IEnumerable SearchEpisodes(string searchQuery); - IEnumerable SearchGenres(string searchQuery); - IEnumerable SearchStudios(string searchQuery); - IEnumerable SearchPeople(string searchQuery); + Task> SearchLibraries(string searchQuery); + Task> SearchCollections(string searchQuery); + Task> SearchShows(string searchQuery); + Task> SearchSeasons(string searchQuery); + Task> SearchEpisodes(string searchQuery); + Task> SearchGenres(string searchQuery); + Task> SearchStudios(string searchQuery); + Task> SearchPeople(string searchQuery); - // Other get helpers - Show GetShowByPath(string path); - IEnumerable GetLibrariesPath(); - IEnumerable GetEpisodes(string showSlug, long seasonNumber); - //Register values - void Register(object obj); - Task Edit(object obj, bool resetOld); - void RegisterShowLinks(Library library, Collection collection, Show show); - Task SaveChanges(); + Task RegisterLibrary(Library library); + Task RegisterCollection(Collection collection); + Task RegisterShow(Show show); + Task RegisterSeason(Season season); + Task RegisterEpisode(Episode episode); + Task RegisterTrack(Track track); + Task RegisterGenre(Genre genre); + Task RegisterStudio(Studio studio); + Task RegisterPeople(People people); - // Validate values - IEnumerable Validate(IEnumerable id); + // Edit values + Task EditLibrary(Library library, bool resetOld); + Task EditCollection(Collection collection, bool resetOld); + Task EditShow(Show show, bool resetOld); + Task EditSeason(Season season, bool resetOld); + Task EditEpisode(Episode episode, bool resetOld); + Task EditTrack(Track track, bool resetOld); + Task EditGenre(Genre genre, bool resetOld); + Task EditStudio(Studio studio, bool resetOld); + Task EditPeople(People people, bool resetOld); + - // Remove values - void RemoveShow(Show show); - void RemoveSeason(Season season); - void RemoveEpisode(Episode episode); + // Delete values + Task DelteLibrary(Library library); + Task DeleteCollection(Collection collection); + Task DeleteShow(Show show); + Task DeleteSeason(Season season); + Task DeleteEpisode(Episode episode); + Task DeleteTrack(Track track); + Task DeleteGenre(Genre genre); + Task DeleteStudio(Studio studio); + Task DeletePeople(People people); } } diff --git a/Kyoo.Common/Controllers/IMetadataProvider.cs b/Kyoo.Common/Controllers/IMetadataProvider.cs index 3a0472b9..1852739f 100644 --- a/Kyoo.Common/Controllers/IMetadataProvider.cs +++ b/Kyoo.Common/Controllers/IMetadataProvider.cs @@ -14,8 +14,8 @@ namespace Kyoo.Controllers Task> SearchShows(string showName, bool isMovie); Task> GetPeople(Show show); - Task GetSeason(Show show, long seasonNumber); + Task GetSeason(Show show, int seasonNumber); - Task GetEpisode(Show show, long seasonNumber, long episodeNumber, long absoluteNumber); + Task GetEpisode(Show show, int seasonNumber, int episodeNumber, int absoluteNumber); } } diff --git a/Kyoo.Common/Controllers/IProviderManager.cs b/Kyoo.Common/Controllers/IProviderManager.cs index 5690842d..b5c3da50 100644 --- a/Kyoo.Common/Controllers/IProviderManager.cs +++ b/Kyoo.Common/Controllers/IProviderManager.cs @@ -10,8 +10,8 @@ namespace Kyoo.Controllers Task CompleteShow(Show show, Library library); Task SearchShow(string showName, bool isMovie, Library library); Task> SearchShows(string showName, bool isMovie, Library library); - Task GetSeason(Show show, long seasonNumber, Library library); - Task GetEpisode(Show show, string episodePath, long seasonNumber, long episodeNumber, long absoluteNumber, Library library); + Task GetSeason(Show show, int seasonNumber, Library library); + Task GetEpisode(Show show, string episodePath, int seasonNumber, int episodeNumber, int absoluteNumber, Library library); Task> GetPeople(Show show, Library library); } } \ No newline at end of file diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs new file mode 100644 index 00000000..0993ca69 --- /dev/null +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Kyoo.Models; + +namespace Kyoo.Controllers +{ + public interface IRepository + { + Task Get(int id); + Task Get(string slug); + Task> Search(string query); + Task> GetAll(); + Task Create([NotNull] T obj); + Task CreateIfNotExists([NotNull] T obj); + Task Edit([NotNull] T edited, bool resetOld); + Task Delete(T obj); + } + + public interface IShowRepository : IRepository + { + Task GetByPath(string path); + Task AddShowLink(int showID, int? libraryID, int? collectionID); + } + + public interface ISeasonRepository : IRepository + { + Task Get(string showSlug, int seasonNumber); + + Task> GetSeasons(int showID); + Task> GetSeasons(string showSlug); + } + + public interface IEpisodeRepository : IRepository + { + Task Get(string showSlug, int seasonNumber, int episodeNumber); + + Task> GetEpisodes(int showID, int seasonNumber); + Task> GetEpisodes(string showSlug, int seasonNumber); + Task> GetEpisodes(int seasonID); + } + + public interface ITrackRepository : IRepository + { + Task Get(int episodeID, string languageTag, bool isForced); + } + public interface ILibraryRepository : IRepository {} + public interface ICollectionRepository : IRepository {} + public interface IGenreRepository : IRepository {} + public interface IStudioRepository : IRepository {} + public interface IPeopleRepository : IRepository {} + public interface IProviderRepository : IRepository {} +} \ No newline at end of file diff --git a/Kyoo.Common/Kyoo.Common.csproj b/Kyoo.Common/Kyoo.Common.csproj index 7799859f..bdd20619 100644 --- a/Kyoo.Common/Kyoo.Common.csproj +++ b/Kyoo.Common/Kyoo.Common.csproj @@ -11,7 +11,7 @@ SDG GPL-3.0-or-later true - 1.0.20 + 1.0.21 diff --git a/Kyoo.Common/Models/Collection.cs b/Kyoo.Common/Models/Collection.cs index c0297521..98881da2 100644 --- a/Kyoo.Common/Models/Collection.cs +++ b/Kyoo.Common/Models/Collection.cs @@ -7,7 +7,7 @@ namespace Kyoo.Models { public class Collection { - [JsonIgnore] public long ID { get; set; } + [JsonIgnore] public int ID { get; set; } public string Slug { get; set; } public string Name { get; set; } public string Poster { get; set; } diff --git a/Kyoo.Common/Models/CollectionLink.cs b/Kyoo.Common/Models/CollectionLink.cs index 24fb6403..cc8aa040 100644 --- a/Kyoo.Common/Models/CollectionLink.cs +++ b/Kyoo.Common/Models/CollectionLink.cs @@ -2,10 +2,10 @@ namespace Kyoo.Models { public class CollectionLink { - public long ID { get; set; } - public long? CollectionID { get; set; } + public int ID { get; set; } + public int? CollectionID { get; set; } public virtual Collection Collection { get; set; } - public long ShowID { get; set; } + public int ShowID { get; set; } public virtual Show Show { get; set; } public CollectionLink() { } diff --git a/Kyoo.Common/Models/Episode.cs b/Kyoo.Common/Models/Episode.cs index ada5a1ea..a00fa12f 100644 --- a/Kyoo.Common/Models/Episode.cs +++ b/Kyoo.Common/Models/Episode.cs @@ -6,21 +6,21 @@ namespace Kyoo.Models { public class Episode { - [JsonIgnore] public long ID { get; set; } - [JsonIgnore] public long ShowID { get; set; } + [JsonIgnore] public int ID { get; set; } + [JsonIgnore] public int ShowID { get; set; } [JsonIgnore] public virtual Show Show { get; set; } - [JsonIgnore] public long? SeasonID { get; set; } + [JsonIgnore] public int? SeasonID { get; set; } [JsonIgnore] public virtual Season Season { get; set; } - public long SeasonNumber { get; set; } - public long EpisodeNumber { get; set; } - public long AbsoluteNumber { get; set; } + public int SeasonNumber { get; set; } + public int EpisodeNumber { get; set; } + public int AbsoluteNumber { get; set; } [JsonIgnore] public string Path { get; set; } public string Title { get; set; } public string Overview { get; set; } public DateTime? ReleaseDate { get; set; } - public long Runtime { get; set; } //This runtime variable should be in minutes + public int Runtime { get; set; } //This runtime variable should be in minutes [JsonIgnore] public string ImgPrimary { get; set; } public virtual IEnumerable ExternalIDs { get; set; } @@ -42,13 +42,13 @@ namespace Kyoo.Models public Episode() { } - public Episode(long seasonNumber, - long episodeNumber, - long absoluteNumber, + public Episode(int seasonNumber, + int episodeNumber, + int absoluteNumber, string title, string overview, DateTime? releaseDate, - long runtime, + int runtime, string imgPrimary, IEnumerable externalIDs) { @@ -63,16 +63,16 @@ namespace Kyoo.Models ExternalIDs = externalIDs; } - public Episode(long showID, - long seasonID, - long seasonNumber, - long episodeNumber, - long absoluteNumber, + public Episode(int showID, + int seasonID, + int seasonNumber, + int episodeNumber, + int absoluteNumber, string path, string title, string overview, DateTime? releaseDate, - long runtime, + int runtime, string imgPrimary, IEnumerable externalIDs) { @@ -90,7 +90,7 @@ namespace Kyoo.Models ExternalIDs = externalIDs; } - public static string GetSlug(string showSlug, long seasonNumber, long episodeNumber) + public static string GetSlug(string showSlug, int seasonNumber, int episodeNumber) { return showSlug + "-s" + seasonNumber + "e" + episodeNumber; } diff --git a/Kyoo.Common/Models/Genre.cs b/Kyoo.Common/Models/Genre.cs index e57cd286..1935189e 100644 --- a/Kyoo.Common/Models/Genre.cs +++ b/Kyoo.Common/Models/Genre.cs @@ -4,7 +4,7 @@ namespace Kyoo.Models { public class Genre { - [JsonIgnore] public long ID { get; set; } + [JsonIgnore] public int ID { get; set; } public string Slug { get; set; } public string Name { get; set; } @@ -24,7 +24,7 @@ namespace Kyoo.Models Name = name; } - public Genre(long id, string slug, string name) + public Genre(int id, string slug, string name) { ID = id; Slug = slug; diff --git a/Kyoo.Common/Models/GenreLink.cs b/Kyoo.Common/Models/GenreLink.cs index cb85d01a..dce3e1bb 100644 --- a/Kyoo.Common/Models/GenreLink.cs +++ b/Kyoo.Common/Models/GenreLink.cs @@ -2,9 +2,9 @@ namespace Kyoo.Models { public class GenreLink { - public long ShowID { get; set; } + public int ShowID { get; set; } public virtual Show Show { get; set; } - public long GenreID { get; set; } + public int GenreID { get; set; } public virtual Genre Genre { get; set; } public GenreLink() {} diff --git a/Kyoo.Common/Models/Library.cs b/Kyoo.Common/Models/Library.cs index d3324b22..84441297 100644 --- a/Kyoo.Common/Models/Library.cs +++ b/Kyoo.Common/Models/Library.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Kyoo.Models.Attributes; using Newtonsoft.Json; @@ -8,7 +7,7 @@ namespace Kyoo.Models { public class Library { - [JsonIgnore] public long ID { get; set; } + [JsonIgnore] public int ID { get; set; } public string Slug { get; set; } public string Name { get; set; } public IEnumerable Paths { get; set; } diff --git a/Kyoo.Common/Models/LibraryLink.cs b/Kyoo.Common/Models/LibraryLink.cs index 36cfc749..22120348 100644 --- a/Kyoo.Common/Models/LibraryLink.cs +++ b/Kyoo.Common/Models/LibraryLink.cs @@ -2,12 +2,12 @@ namespace Kyoo.Models { public class LibraryLink { - public long ID { get; set; } - public long LibraryID { get; set; } + public int ID { get; set; } + public int LibraryID { get; set; } public virtual Library Library { get; set; } - public long? ShowID { get; set; } + public int? ShowID { get; set; } public virtual Show Show { get; set; } - public long? CollectionID { get; set; } + public int? CollectionID { get; set; } public virtual Collection Collection { get; set; } public LibraryLink() { } diff --git a/Kyoo.Common/Models/MetadataID.cs b/Kyoo.Common/Models/MetadataID.cs index b1bd7a72..649ec157 100644 --- a/Kyoo.Common/Models/MetadataID.cs +++ b/Kyoo.Common/Models/MetadataID.cs @@ -4,20 +4,20 @@ namespace Kyoo.Models { public class MetadataID { - [JsonIgnore] public long ID { get; set; } - [JsonIgnore] public long ProviderID { get; set; } + [JsonIgnore] public int ID { get; set; } + [JsonIgnore] public int ProviderID { get; set; } public virtual ProviderID Provider {get; set; } - [JsonIgnore] public long? ShowID { get; set; } + [JsonIgnore] public int? ShowID { get; set; } [JsonIgnore] public virtual Show Show { get; set; } - [JsonIgnore] public long? EpisodeID { get; set; } + [JsonIgnore] public int? EpisodeID { get; set; } [JsonIgnore] public virtual Episode Episode { get; set; } - [JsonIgnore] public long? SeasonID { get; set; } + [JsonIgnore] public int? SeasonID { get; set; } [JsonIgnore] public virtual Season Season { get; set; } - [JsonIgnore] public long? PeopleID { get; set; } + [JsonIgnore] public int? PeopleID { get; set; } [JsonIgnore] public virtual People People { get; set; } public string DataID { get; set; } diff --git a/Kyoo.Common/Models/People.cs b/Kyoo.Common/Models/People.cs index 8dc17f9d..6514dc9f 100644 --- a/Kyoo.Common/Models/People.cs +++ b/Kyoo.Common/Models/People.cs @@ -6,7 +6,7 @@ namespace Kyoo.Models { public class People { - public long ID { get; set; } + public int ID { get; set; } public string Slug { get; set; } public string Name { get; set; } [JsonIgnore] public string ImgPrimary { get; set; } diff --git a/Kyoo.Common/Models/PeopleLink.cs b/Kyoo.Common/Models/PeopleLink.cs index ef6ef3af..509f2b93 100644 --- a/Kyoo.Common/Models/PeopleLink.cs +++ b/Kyoo.Common/Models/PeopleLink.cs @@ -5,8 +5,8 @@ namespace Kyoo.Models { public class PeopleLink { - [JsonIgnore] public long ID { get; set; } - [JsonIgnore] public long PeopleID { get; set; } + [JsonIgnore] public int ID { get; set; } + [JsonIgnore] public int PeopleID { get; set; } [JsonIgnore] public virtual People People { get; set; } public string Slug @@ -27,7 +27,7 @@ namespace Kyoo.Models set => People.ExternalIDs = value; } - [JsonIgnore] public long ShowID { get; set; } + [JsonIgnore] public int ShowID { get; set; } [JsonIgnore] public virtual Show Show { get; set; } public string Role { get; set; } public string Type { get; set; } diff --git a/Kyoo.Common/Models/ProviderID.cs b/Kyoo.Common/Models/ProviderID.cs index e7d76268..b68785b1 100644 --- a/Kyoo.Common/Models/ProviderID.cs +++ b/Kyoo.Common/Models/ProviderID.cs @@ -4,13 +4,13 @@ namespace Kyoo.Models { public class ProviderID { - [JsonIgnore] public long ID { get; set; } + [JsonIgnore] public int ID { get; set; } public string Name { get; set; } public string Logo { get; set; } public ProviderID() { } - public ProviderID(long id, string name, string logo) + public ProviderID(int id, string name, string logo) { ID = id; Name = name; diff --git a/Kyoo.Common/Models/ProviderLink.cs b/Kyoo.Common/Models/ProviderLink.cs index 088e4f80..6bc50b1a 100644 --- a/Kyoo.Common/Models/ProviderLink.cs +++ b/Kyoo.Common/Models/ProviderLink.cs @@ -4,10 +4,10 @@ namespace Kyoo.Models { public class ProviderLink { - [JsonIgnore] public long ID { get; set; } - [JsonIgnore] public long ProviderID { get; set; } + [JsonIgnore] public int ID { get; set; } + [JsonIgnore] public int ProviderID { get; set; } [JsonIgnore] public virtual ProviderID Provider { get; set; } - [JsonIgnore] public long? LibraryID { get; set; } + [JsonIgnore] public int? LibraryID { get; set; } [JsonIgnore] public virtual Library Library { get; set; } public ProviderLink() { } diff --git a/Kyoo.Common/Models/Season.cs b/Kyoo.Common/Models/Season.cs index 0f306349..8d4d9052 100644 --- a/Kyoo.Common/Models/Season.cs +++ b/Kyoo.Common/Models/Season.cs @@ -5,15 +5,15 @@ namespace Kyoo.Models { public class Season { - [JsonIgnore] public long ID { get; set; } - [JsonIgnore] public long ShowID { get; set; } + [JsonIgnore] public int ID { get; set; } + [JsonIgnore] public int ShowID { get; set; } - public long SeasonNumber { get; set; } = -1; + public int SeasonNumber { get; set; } = -1; - public string Slug => $"{Show.Title}-s{SeasonNumber}"; + public string Slug => $"{Show.Slug}-s{SeasonNumber}"; public string Title { get; set; } public string Overview { get; set; } - public long? Year { get; set; } + public int? Year { get; set; } [JsonIgnore] public string ImgPrimary { get; set; } public virtual IEnumerable ExternalIDs { get; set; } @@ -23,11 +23,11 @@ namespace Kyoo.Models public Season() { } - public Season(long showID, - long seasonNumber, + public Season(int showID, + int seasonNumber, string title, string overview, - long? year, + int? year, string imgPrimary, IEnumerable externalIDs) { diff --git a/Kyoo.Common/Models/Show.cs b/Kyoo.Common/Models/Show.cs index 4801fb35..a79ef71e 100644 --- a/Kyoo.Common/Models/Show.cs +++ b/Kyoo.Common/Models/Show.cs @@ -7,7 +7,7 @@ namespace Kyoo.Models { public class Show : IOnMerge { - [JsonIgnore] public long ID { get; set; } + [JsonIgnore] public int ID { get; set; } public string Slug { get; set; } public string Title { get; set; } @@ -17,8 +17,8 @@ namespace Kyoo.Models public Status? Status { get; set; } public string TrailerUrl { get; set; } - public long? StartYear { get; set; } - public long? EndYear { get; set; } + public int? StartYear { get; set; } + public int? EndYear { get; set; } public string Poster { get; set; } public string Logo { get; set; } @@ -36,6 +36,7 @@ namespace Kyoo.Models set => GenreLinks = value?.Select(x => new GenreLink(this, x)).ToList(); } [NotMergable] [JsonIgnore] public virtual IEnumerable GenreLinks { get; set; } + [JsonIgnore] public int? StudioID { get; set; } public virtual Studio Studio { get; set; } [JsonIgnore] public virtual IEnumerable People { get; set; } [JsonIgnore] public virtual IEnumerable Seasons { get; set; } @@ -50,8 +51,8 @@ namespace Kyoo.Models string trailerUrl, IEnumerable genres, Status? status, - long? startYear, - long? endYear, + int? startYear, + int? endYear, IEnumerable externalIDs) { Slug = slug; @@ -75,8 +76,8 @@ namespace Kyoo.Models string overview, string trailerUrl, Status? status, - long? startYear, - long? endYear, + int? startYear, + int? endYear, string poster, string logo, string backdrop, diff --git a/Kyoo.Common/Models/Studio.cs b/Kyoo.Common/Models/Studio.cs index f02637fa..ec37121a 100644 --- a/Kyoo.Common/Models/Studio.cs +++ b/Kyoo.Common/Models/Studio.cs @@ -5,7 +5,7 @@ namespace Kyoo.Models { public class Studio { - [JsonIgnore] public long ID { get; set; } + [JsonIgnore] public int ID { get; set; } public string Slug { get; set; } public string Name { get; set; } diff --git a/Kyoo.Common/Models/Task.cs b/Kyoo.Common/Models/Task.cs index f00b24c2..76cfbc52 100644 --- a/Kyoo.Common/Models/Task.cs +++ b/Kyoo.Common/Models/Task.cs @@ -14,7 +14,7 @@ namespace Kyoo.Models public bool RunOnStartup { get; } public int Priority { get; } public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null); - public IEnumerable GetPossibleParameters(); + public Task> GetPossibleParameters(); public int? Progress(); } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Track.cs b/Kyoo.Common/Models/Track.cs index 7dc52ba3..9741330e 100644 --- a/Kyoo.Common/Models/Track.cs +++ b/Kyoo.Common/Models/Track.cs @@ -55,8 +55,8 @@ namespace Kyoo.Models public class Track : Stream { - [JsonIgnore] public long ID { get; set; } - [JsonIgnore] public long EpisodeID { get; set; } + [JsonIgnore] public int ID { get; set; } + [JsonIgnore] public int EpisodeID { get; set; } public bool IsDefault { get => isDefault; diff --git a/Kyoo.Common/Models/WatchItem.cs b/Kyoo.Common/Models/WatchItem.cs index 3278f313..10c91350 100644 --- a/Kyoo.Common/Models/WatchItem.cs +++ b/Kyoo.Common/Models/WatchItem.cs @@ -7,12 +7,12 @@ namespace Kyoo.Models { public class WatchItem { - [JsonIgnore] public readonly long EpisodeID = -1; + [JsonIgnore] public readonly int EpisodeID = -1; public string ShowTitle; public string ShowSlug; - public long SeasonNumber; - public long EpisodeNumber; + public int SeasonNumber; + public int EpisodeNumber; public string Title; public string Link; public DateTime? ReleaseDate; @@ -28,11 +28,11 @@ namespace Kyoo.Models public WatchItem() { } - public WatchItem(long episodeID, + public WatchItem(int episodeID, string showTitle, string showSlug, - long seasonNumber, - long episodeNumber, + int seasonNumber, + int episodeNumber, string title, DateTime? releaseDate, string path) @@ -50,11 +50,11 @@ namespace Kyoo.Models Link = Episode.GetSlug(ShowSlug, seasonNumber, episodeNumber); } - public WatchItem(long episodeID, + public WatchItem(int episodeID, string showTitle, string showSlug, - long seasonNumber, - long episodeNumber, + int seasonNumber, + int episodeNumber, string title, DateTime? releaseDate, string path, diff --git a/Kyoo/Controllers/LibraryManager.cs b/Kyoo/Controllers/LibraryManager.cs index 8d7aaaf2..c4f1432a 100644 --- a/Kyoo/Controllers/LibraryManager.cs +++ b/Kyoo/Controllers/LibraryManager.cs @@ -1,394 +1,366 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Kyoo.Models; -using Kyoo.Models.Exceptions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; namespace Kyoo.Controllers { public class LibraryManager : ILibraryManager { - private const int MaxSaveRetry = 3; - private readonly DatabaseContext _database; + private readonly ILibraryRepository _libraries; + private readonly ICollectionRepository _collections; + private readonly IShowRepository _shows; + private readonly ISeasonRepository _seasons; + private readonly IEpisodeRepository _episodes; + private readonly ITrackRepository _tracks; + private readonly IGenreRepository _genres; + private readonly IStudioRepository _studios; + private readonly IPeopleRepository _people; + private readonly IProviderRepository _providers; - - public LibraryManager(DatabaseContext database) + public LibraryManager(ILibraryRepository libraries, + ICollectionRepository collections, + IShowRepository shows, + ISeasonRepository seasons, + IEpisodeRepository episodes, + ITrackRepository tracks, + IGenreRepository genres, + IStudioRepository studios, + IProviderRepository providers, + IPeopleRepository people) { - _database = database; + _libraries = libraries; + _collections = collections; + _shows = shows; + _seasons = seasons; + _episodes = episodes; + _tracks = tracks; + _genres = genres; + _studios = studios; + _providers = providers; + _people = people; } - #region GetBySlug - public Library GetLibrary(string librarySlug) + public Task GetLibrary(string slug) { - return _database.Libraries.FirstOrDefault(library => library.Slug == librarySlug); + return _libraries.Get(slug); + } + + public Task GetCollection(string slug) + { + return _collections.Get(slug); + } + + public Task GetShow(string slug) + { + return _shows.Get(slug); + } + + public Task GetSeason(string showSlug, int seasonNumber) + { + return _seasons.Get(showSlug, seasonNumber); + } + + public Task GetEpisode(string showSlug, int seasonNumber, int episodeNumber) + { + return _episodes.Get(showSlug, seasonNumber, episodeNumber); + } + + public Task GetMovieEpisode(string movieSlug) + { + return _episodes.Get(movieSlug); + } + + public Task GetTrack(int id) + { + return _tracks.Get(id); } - public Collection GetCollection(string slug) + public Task GetTrack(int episodeID, string language, bool isForced) { - return _database.Collections.FirstOrDefault(col => col.Slug == slug); + return _tracks.Get(episodeID, language, isForced); } - public Show GetShow(string slug) + public Task GetGenre(string slug) { - return _database.Shows.FirstOrDefault(show => show.Slug == slug); + return _genres.Get(slug); } - public Season GetSeason(string showSlug, long seasonNumber) + public Task GetStudio(string slug) { - return _database.Seasons.FirstOrDefault(x => x.Show.Slug == showSlug && x.SeasonNumber == seasonNumber); + return _studios.Get(slug); } - public Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber) + public Task GetPeople(string slug) { - return _database.Episodes.FirstOrDefault(x => x.EpisodeNumber == episodeNumber - && x.SeasonNumber == seasonNumber - && x.Show.Slug == showSlug); + return _people.Get(slug); + } + + public Task> GetLibraries() + { + return _libraries.GetAll(); + } + + public Task> GetCollections() + { + return _collections.GetAll(); + } + + public Task> GetShows() + { + return _shows.GetAll(); + } + + public Task> GetSeasons() + { + return _seasons.GetAll(); + } + + public Task> GetEpisodes() + { + return _episodes.GetAll(); + } + + public Task> GetTracks() + { + return _tracks.GetAll(); + } + + public Task> GetStudios() + { + return _studios.GetAll(); + } + + public Task> GetPeoples() + { + return _people.GetAll(); + } + + public Task> GetGenres() + { + return _genres.GetAll(); + } + + public Task> GetProviders() + { + return _providers.GetAll(); + } + + public Task> GetSeasons(int showID) + { + return _seasons.GetSeasons(showID); + } + + public Task> GetSeasons(string showSlug) + { + return _seasons.GetSeasons(showSlug); + } + + public Task> GetEpisodes(int showID, int seasonNumber) + { + return _episodes.GetEpisodes(showID, seasonNumber); + } + + public Task> GetEpisodes(string showSlug, int seasonNumber) + { + return _episodes.GetEpisodes(showSlug, seasonNumber); + } + + public Task> GetEpisodes(int seasonID) + { + return _episodes.GetEpisodes(seasonID); + } + + public Task GetShowByPath(string path) + { + return _shows.GetByPath(path); + } + + public Task AddShowLink(int showID, int? libraryID, int? collectionID) + { + return _shows.AddShowLink(showID, libraryID, collectionID); + } + + public Task AddShowLink(Show show, Library library, Collection collection) + { + if (show == null) + throw new ArgumentNullException(nameof(show)); + return AddShowLink(show.ID, library?.ID, collection?.ID); } - public Episode GetMovieEpisode(string movieSlug) + public Task> SearchLibraries(string searchQuery) { - return _database.Episodes.FirstOrDefault(x => x.Show.Slug == movieSlug); + return _libraries.Search(searchQuery); } - public Genre GetGenre(string slug) + public Task> SearchCollections(string searchQuery) { - return _database.Genres.FirstOrDefault(genre => genre.Slug == slug); + return _collections.Search(searchQuery); } - public Studio GetStudio(string slug) + public Task> SearchShows(string searchQuery) { - return _database.Studios.FirstOrDefault(studio => studio.Slug == slug); + return _shows.Search(searchQuery); } - public People GetPeople(string slug) + public Task> SearchSeasons(string searchQuery) { - return _database.Peoples.FirstOrDefault(people => people.Slug == slug); + return _seasons.Search(searchQuery); } - public ProviderID GetProvider(string name) + public Task> SearchEpisodes(string searchQuery) { - return _database.Providers.FirstOrDefault(x => x.Name == name); + return _episodes.Search(searchQuery); } - #endregion - - #region GetAll - public IEnumerable GetLibraries() + public Task> SearchGenres(string searchQuery) { - return _database.Libraries; + return _genres.Search(searchQuery); } - public IEnumerable GetCollections() + public Task> SearchStudios(string searchQuery) { - return _database.Collections; + return _studios.Search(searchQuery); } - public IEnumerable GetShows() + public Task> SearchPeople(string searchQuery) { - return _database.Shows; + return _people.Search(searchQuery); } - public IEnumerable GetEpisodes() + public Task RegisterLibrary(Library library) { - return _database.Episodes; - } - - public IEnumerable GetGenres() - { - return _database.Genres; - } - - public IEnumerable GetStudios() - { - return _database.Studios; + return _libraries.Create(library); } - public IEnumerable GetPeoples() + public Task RegisterCollection(Collection collection) { - return _database.Peoples; + return _collections.Create(collection); } - public IEnumerable GetTracks() + public Task RegisterShow(Show show) { - return _database.Tracks; + return _shows.Create(show); } - #endregion - - #region GetHelper - public IEnumerable GetLibrariesPath() + public Task RegisterSeason(Season season) { - IEnumerable paths = new List(); - return Enumerable.Aggregate(_database.Libraries, paths, (current, lib) => current.Concat(lib.Paths)); - } - - public Show GetShowByPath(string path) - { - return _database.Shows.FirstOrDefault(show => show.Path == path); + return _seasons.Create(season); } - public IEnumerable GetEpisodes(string showSlug, long seasonNumber) + public Task RegisterEpisode(Episode episode) { - return _database.Episodes.Where(x => x.Show.Slug == showSlug && x.SeasonNumber == seasonNumber); - } - #endregion - - #region Search - public IEnumerable SearchCollections(string searchQuery) - { - return _database.Collections.Where(collection => EF.Functions.Like(collection.Name, $"%{searchQuery}%")) - .Take(20); - } - - public IEnumerable SearchShows(string searchQuery) - { - return _database.Shows.FromSqlInterpolated($@"SELECT * FROM Shows WHERE Shows.Title LIKE {$"%{searchQuery}%"} - OR Shows.Aliases LIKE {$"%{searchQuery}%"}").Take(20); - } - - public IEnumerable SearchEpisodes(string searchQuery) - { - return _database.Episodes.Where(x => EF.Functions.Like(x.Title, $"%{searchQuery}%")).Take(20); + return _episodes.Create(episode); } - public IEnumerable SearchGenres(string searchQuery) + public Task RegisterTrack(Track track) { - return _database.Genres.Where(genre => EF.Functions.Like(genre.Name, $"%{searchQuery}%")) - .Take(20); - } - - public IEnumerable SearchStudios(string searchQuery) - { - return _database.Studios.Where(studio => EF.Functions.Like(studio.Name, $"%{searchQuery}%")) - .Take(20); - } - - public IEnumerable SearchPeople(string searchQuery) - { - return _database.Peoples.Where(people => EF.Functions.Like(people.Name, $"%{searchQuery}%")) - .OrderBy(x => x.ImgPrimary == null) - .ThenBy(x => x.Name) - .Take(20); - } - #endregion - - #region Register - public void Register(object obj) - { - if (obj == null) - return; - ValidateRootEntry(_database.Entry(obj), entry => - { - if (entry.State != EntityState.Detached) - return false; - - entry.State = EntityState.Added; - return true; - }); + return _tracks.Create(track); } - public void RegisterShowLinks(Library library, Collection collection, Show show) + public Task RegisterGenre(Genre genre) { - if (collection != null) - { - _database.LibraryLinks.AddIfNotExist(new LibraryLink {Library = library, Collection = collection}, - x => x.Library == library && x.Collection == collection && x.ShowID == null); - _database.CollectionLinks.AddIfNotExist(new CollectionLink { Collection = collection, Show = show}, - x => x.Collection == collection && x.Show == show); - } - else - _database.LibraryLinks.AddIfNotExist(new LibraryLink {Library = library, Show = show}, - x => x.Library == library && x.Collection == null && x.Show == show); + return _genres.Create(genre); } - public Task SaveChanges() + public Task RegisterStudio(Studio studio) { - return SaveChanges(0); + return _studios.Create(studio); } - private async Task SaveChanges(int retryCount) + public Task RegisterPeople(People people) { - ValidateChanges(); - try - { - await _database.SaveChangesAsync(); - } - catch (DbUpdateException) - { - if (retryCount < MaxSaveRetry) - await SaveChanges(retryCount + 1); - else - throw; - } - } - - public async Task Edit(object obj, bool resetOld) - { - _database.ChangeTracker.LazyLoadingEnabled = false; - _database.ChangeTracker.AutoDetectChangesEnabled = false; - - try - { - object existing = FindExisting(obj); - - if (existing == null) - throw new ItemNotFound($"No existing object (of type {obj.GetType().Name}) found on the databse."); - - if (resetOld) - Utility.Nullify(existing); - Utility.Merge(existing, obj); - - ValidateRootEntry(_database.Entry(existing), entry => entry.State != EntityState.Added); - - _database.ChangeTracker.DetectChanges(); - await _database.SaveChangesAsync(); - } - finally - { - _database.ChangeTracker.LazyLoadingEnabled = true; - _database.ChangeTracker.AutoDetectChangesEnabled = true; - } - } - #endregion - - #region ValidateValue - private void ValidateChanges() - { - _database.ChangeTracker.AutoDetectChangesEnabled = false; - try - { - foreach (EntityEntry sourceEntry in _database.ChangeTracker.Entries()) - { - if (sourceEntry.State != EntityState.Added && sourceEntry.State != EntityState.Modified) - continue; - - foreach (NavigationEntry navigation in sourceEntry.Navigations) - ValidateNavigation(navigation); - } - } - finally - { - _database.ChangeTracker.AutoDetectChangesEnabled = true; - _database.ChangeTracker.DetectChanges(); - } - } - - private void ValidateRootEntry(EntityEntry entry, Func shouldRun) - { - if (!shouldRun.Invoke(entry)) - return; - foreach (NavigationEntry navigation in entry.Navigations) - { - ValidateNavigation(navigation); - if (navigation.CurrentValue == null) - continue; - if (navigation.Metadata.IsCollection()) - { - IEnumerable entities = (IEnumerable)navigation.CurrentValue; - foreach (object childEntry in entities) - ValidateRootEntry(_database.Entry(childEntry), shouldRun); - } - else - ValidateRootEntry(_database.Entry(navigation.CurrentValue), shouldRun); - } + return _people.Create(people); } - private void ValidateNavigation(NavigationEntry navigation) + public Task EditLibrary(Library library, bool resetOld) { - object oldValue = navigation.CurrentValue; - if (oldValue == null) - return; - object newValue = Validate(oldValue); - if (ReferenceEquals(oldValue, newValue)) - return; - navigation.CurrentValue = newValue; - if (!navigation.Metadata.IsCollection()) - _database.Entry(oldValue).State = EntityState.Detached; - } - - private T Validate(T obj) where T : class - { - switch (obj) - { - case null: - return null; - case IEnumerable enumerable: - return (T)Utility.RunGenericMethod( - this, - "ValidateList", - Utility.GetEnumerableType(enumerable), new [] {obj}); - } - - EntityState state = _database.Entry(obj).State; - if (state != EntityState.Added && state != EntityState.Detached) - return obj; - - return (T)(FindExisting(obj) ?? obj); + return _libraries.Edit(library, resetOld); } - public IEnumerable ValidateList(IEnumerable list) where T : class + public Task EditCollection(Collection collection, bool resetOld) { - return list.Select(x => - { - T tmp = Validate(x); - if (tmp != x) - _database.Entry(x).State = EntityState.Detached; - return tmp ?? x; - })/*.GroupBy(GetSlug).Select(x => x.First()).Where(x => x != null)*/.ToList(); + return _collections.Edit(collection, resetOld); } - private object FindExisting(object obj) + public Task EditShow(Show show, bool resetOld) { - return obj switch - { - Library library => GetLibrary(library.Slug), - Collection collection => GetCollection(collection.Slug), - Show show => GetShow(show.Slug), - Season season => GetSeason(season.Show.Slug, season.SeasonNumber), - Episode episode => GetEpisode(episode.Show.Slug, episode.SeasonNumber, episode.EpisodeNumber), - Studio studio => GetStudio(studio.Slug), - People people => GetPeople(people.Slug), - Genre genre => GetGenre(genre.Slug), - ProviderID provider => GetProvider(provider.Name), - _ => null - }; + return _shows.Edit(show, resetOld); } - public IEnumerable Validate(IEnumerable ids) + public Task EditSeason(Season season, bool resetOld) { - return ids?.Select(x => - { - x.Provider = _database.Providers.FirstOrDefault(y => y.Name == x.Provider.Name) ?? x.Provider; - return x; - }).GroupBy(x => x.Provider.Name).Select(x => x.First()).ToList(); - } - #endregion - - #region Remove - public void RemoveShow(Show show) - { - if (_database.Entry(show).State == EntityState.Detached) - _database.Shows.Attach(show); - _database.Shows.Remove(show); + return _seasons.Edit(season, resetOld); } - public void RemoveSeason(Season season) + public Task EditEpisode(Episode episode, bool resetOld) { - if (_database.Entry(season).State == EntityState.Detached) - _database.Seasons.Attach(season); - _database.Seasons.Remove(season); + return _episodes.Edit(episode, resetOld); } - public void RemoveEpisode(Episode episode) + public Task EditTrack(Track track, bool resetOld) { - if (_database.Entry(episode).State == EntityState.Detached) - _database.Episodes.Attach(episode); - _database.Episodes.Remove(episode); + return _tracks.Edit(track, resetOld); + } + + public Task EditGenre(Genre genre, bool resetOld) + { + return _genres.Edit(genre, resetOld); + } + + public Task EditStudio(Studio studio, bool resetOld) + { + return _studios.Edit(studio, resetOld); + } + + public Task EditPeople(People people, bool resetOld) + { + return _people.Edit(people, resetOld); + } + + public Task DelteLibrary(Library library) + { + return _libraries.Delete(library); + } + + public Task DeleteCollection(Collection collection) + { + return _collections.Delete(collection); + } + + public Task DeleteShow(Show show) + { + return _shows.Delete(show); + } + + public Task DeleteSeason(Season season) + { + return _seasons.Delete(season); + } + + public Task DeleteEpisode(Episode episode) + { + return _episodes.Delete(episode); + } + + public Task DeleteTrack(Track track) + { + return _tracks.Delete(track); + } + + public Task DeleteGenre(Genre genre) + { + return _genres.Delete(genre); + } + + public Task DeleteStudio(Studio studio) + { + return _studios.Delete(studio); + } + + public Task DeletePeople(People people) + { + return _people.Delete(people); } - #endregion } } diff --git a/Kyoo/Controllers/ProviderManager.cs b/Kyoo/Controllers/ProviderManager.cs index 5dc6791c..663898d1 100644 --- a/Kyoo/Controllers/ProviderManager.cs +++ b/Kyoo/Controllers/ProviderManager.cs @@ -113,13 +113,14 @@ namespace Kyoo.Controllers }); } - public async Task GetSeason(Show show, long seasonNumber, Library library) + public async Task GetSeason(Show show, int seasonNumber, Library library) { Season season = await GetMetadata( provider => provider.GetSeason(show, seasonNumber), library, $"the season {seasonNumber} of {show.Title}"); season.Show = show; + season.ShowID = show.ID; season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber; season.Title ??= $"Season {season.SeasonNumber}"; return season; @@ -127,9 +128,9 @@ namespace Kyoo.Controllers public async Task GetEpisode(Show show, string episodePath, - long seasonNumber, - long episodeNumber, - long absoluteNumber, + int seasonNumber, + int episodeNumber, + int absoluteNumber, Library library) { Episode episode = await GetMetadata( @@ -137,6 +138,7 @@ namespace Kyoo.Controllers library, "an episode"); episode.Show = show; + episode.ShowID = show.ID; episode.Path = episodePath; episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber; episode.EpisodeNumber = episode.EpisodeNumber != -1 ? episode.EpisodeNumber : episodeNumber; @@ -155,6 +157,7 @@ namespace Kyoo.Controllers .Select(x => { x.Show = show; + x.ShowID = show.ID; return x; }).ToList(); } diff --git a/Kyoo/Controllers/Repositories/CollectionRepository.cs b/Kyoo/Controllers/Repositories/CollectionRepository.cs new file mode 100644 index 00000000..c09bbc94 --- /dev/null +++ b/Kyoo/Controllers/Repositories/CollectionRepository.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; + +namespace Kyoo.Controllers +{ + public class CollectionRepository : ICollectionRepository + { + private readonly DatabaseContext _database; + + + public CollectionRepository(DatabaseContext database) + { + _database = database; + } + + public Task Get(int id) + { + return _database.Collections.FirstOrDefaultAsync(x => x.ID == id); + } + + public Task Get(string slug) + { + return _database.Collections.FirstOrDefaultAsync(x => x.Slug == slug); + } + + public async Task> Search(string query) + { + return await _database.Collections + .Where(x => EF.Functions.Like(x.Name, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Collections.ToListAsync(); + } + + public async Task Create(Collection obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await _database.Collections.AddAsync(obj); + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(Collection obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Collection old = await Get(obj.Slug); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(Collection edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Collection old = await Get(edited.Slug); + + if (old == null) + throw new ItemNotFound($"No collection found with the slug {edited.Slug}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + + await _database.SaveChangesAsync(); + } + + public async Task Delete(Collection obj) + { + _database.Collections.Remove(obj); + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs new file mode 100644 index 00000000..231107b6 --- /dev/null +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Controllers +{ + public class EpisodeRepository : IEpisodeRepository + { + private readonly DatabaseContext _database; + private readonly IServiceProvider _serviceProvider; + + + public EpisodeRepository(DatabaseContext database, IServiceProvider serviceProvider) + { + _database = database; + _serviceProvider = serviceProvider; + } + + public async Task Get(int id) + { + return await _database.Episodes.FirstOrDefaultAsync(x => x.ID == id); + } + + public Task Get(string slug) + { + int sIndex = slug.IndexOf("-s", StringComparison.Ordinal); + int eIndex = slug.IndexOf("-e", StringComparison.Ordinal); + if (sIndex == -1 || eIndex == -1 || eIndex < sIndex) + throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}"); + string showSlug = slug.Substring(0, sIndex); + if (!int.TryParse(slug.Substring(sIndex + 2), out int seasonNumber)) + throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}"); + if (!int.TryParse(slug.Substring(eIndex + 2), out int episodeNumber)) + throw new InvalidOperationException("Invalid episode slug. Format: {showSlug}-s{seasonNumber}-e{episodeNumber}"); + return Get(showSlug, seasonNumber, episodeNumber); + } + + public async Task Get(string showSlug, int seasonNumber, int episodeNumber) + { + return await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug + && x.SeasonNumber == seasonNumber + && x.EpisodeNumber == episodeNumber); + } + + public async Task> Search(string query) + { + return await _database.Episodes + .Where(x => EF.Functions.Like(x.Title, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Episodes.ToListAsync(); + } + + public async Task Create(Episode obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await Validate(obj); + _database.Entry(obj).State = EntityState.Added; + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Added; + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(Episode obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Episode old = await Get(obj.Slug); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(Episode edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Episode old = await Get(edited.Slug); + + if (old == null) + throw new ItemNotFound($"No episode found with the slug {edited.Slug}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + + await Validate(old); + await _database.SaveChangesAsync(); + } + + private async Task Validate(Episode obj) + { + if (obj.ShowID <= 0) + throw new InvalidOperationException($"Can't store an episode not related to any show (showID: {obj.ShowID})."); + + if (obj.ExternalIDs != null) + { + obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x => + { + using IServiceScope serviceScope = _serviceProvider.CreateScope(); + IProviderRepository providers = serviceScope.ServiceProvider.GetService(); + + x.ProviderID = await providers.CreateIfNotExists(x.Provider); + return x; + }))).ToList(); + } + } + + public async Task Delete(Episode obj) + { + _database.Episodes.Remove(obj); + await _database.SaveChangesAsync(); + } + + public async Task> GetEpisodes(int showID, int seasonNumber) + { + return await _database.Episodes.Where(x => x.ShowID == showID + && x.SeasonNumber == seasonNumber).ToListAsync(); + } + + public async Task> GetEpisodes(string showSlug, int seasonNumber) + { + return await _database.Episodes.Where(x => x.Show.Slug == showSlug + && x.SeasonNumber == seasonNumber).ToListAsync(); + } + + public async Task> GetEpisodes(int seasonID) + { + return await _database.Episodes.Where(x => x.SeasonID == seasonID).ToListAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/GenreRepository.cs b/Kyoo/Controllers/Repositories/GenreRepository.cs new file mode 100644 index 00000000..11931c10 --- /dev/null +++ b/Kyoo/Controllers/Repositories/GenreRepository.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; + +namespace Kyoo.Controllers +{ + public class GenreRepository : IGenreRepository + { + private readonly DatabaseContext _database; + + + public GenreRepository(DatabaseContext database) + { + _database = database; + } + + public async Task Get(int id) + { + return await _database.Genres.FirstOrDefaultAsync(x => x.ID == id); + } + + public async Task Get(string slug) + { + return await _database.Genres.FirstOrDefaultAsync(x => x.Slug == slug); + } + + public async Task> Search(string query) + { + return await _database.Genres + .Where(genre => EF.Functions.Like(genre.Name, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Genres.ToListAsync(); + } + + public async Task Create(Genre obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await _database.Genres.AddAsync(obj); + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(Genre obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Genre old = await Get(obj.Slug); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(Genre edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Genre old = await Get(edited.Slug); + + if (old == null) + throw new ItemNotFound($"No genre found with the slug {edited.Slug}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + await _database.SaveChangesAsync(); + } + + public async Task Delete(Genre obj) + { + _database.Genres.Remove(obj); + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/LibraryRepository.cs b/Kyoo/Controllers/Repositories/LibraryRepository.cs new file mode 100644 index 00000000..4df5a1d0 --- /dev/null +++ b/Kyoo/Controllers/Repositories/LibraryRepository.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Controllers +{ + public class LibraryRepository : ILibraryRepository + { + private readonly DatabaseContext _database; + private readonly IServiceProvider _serviceProvider; + + + public LibraryRepository(DatabaseContext database, IServiceProvider serviceProvider) + { + _database = database; + _serviceProvider = serviceProvider; + } + + public Task Get(int id) + { + return _database.Libraries.FirstOrDefaultAsync(x => x.ID == id); + } + + public Task Get(string slug) + { + return _database.Libraries.FirstOrDefaultAsync(x => x.Slug == slug); + } + + public async Task> Search(string query) + { + return await _database.Libraries + .Where(x => EF.Functions.Like(x.Name, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Libraries.ToListAsync(); + } + + public async Task Create(Library obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await Validate(obj); + _database.Entry(obj).State = EntityState.Added; + if (obj.ProviderLinks != null) + foreach (ProviderLink entry in obj.ProviderLinks) + _database.Entry(entry).State = EntityState.Added; + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(Library obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Library old = await Get(obj.Name); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(Library edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Library old = await Get(edited.Name); + + if (old == null) + throw new ItemNotFound($"No library found with the name {edited.Name}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + await Validate(old); + await _database.SaveChangesAsync(); + } + + private async Task Validate(Library obj) + { + obj.ProviderLinks = (await Task.WhenAll(obj.ProviderLinks.Select(async x => + { + using IServiceScope serviceScope = _serviceProvider.CreateScope(); + IProviderRepository providers = serviceScope.ServiceProvider.GetService(); + + x.ProviderID = await providers.CreateIfNotExists(x.Provider); + return x; + }))).ToList(); + } + + public async Task Delete(Library obj) + { + _database.Libraries.Remove(obj); + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/PeopleRepository.cs b/Kyoo/Controllers/Repositories/PeopleRepository.cs new file mode 100644 index 00000000..26bcc825 --- /dev/null +++ b/Kyoo/Controllers/Repositories/PeopleRepository.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Controllers +{ + public class PeopleRepository : IPeopleRepository + { + private readonly DatabaseContext _database; + private readonly IServiceProvider _serviceProvider; + + public PeopleRepository(DatabaseContext database, IServiceProvider serviceProvider) + { + _database = database; + _serviceProvider = serviceProvider; + } + + public Task Get(int id) + { + return _database.Peoples.FirstOrDefaultAsync(x => x.ID == id); + } + + public Task Get(string slug) + { + return _database.Peoples.FirstOrDefaultAsync(x => x.Slug == slug); + } + + public async Task> Search(string query) + { + return await _database.Peoples + .Where(people => EF.Functions.Like(people.Name, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Peoples.ToListAsync(); + } + + public async Task Create(People obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await Validate(obj); + _database.Entry(obj).State = EntityState.Added; + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Added; + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(People obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + People old = await Get(obj.Slug); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(People edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + People old = await Get(edited.Slug); + + if (old == null) + throw new ItemNotFound($"No people found with the slug {edited.Slug}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + await Validate(old); + await _database.SaveChangesAsync(); + } + + private async Task Validate(People obj) + { + obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x => + { + using IServiceScope serviceScope = _serviceProvider.CreateScope(); + IProviderRepository providers = serviceScope.ServiceProvider.GetService(); + + x.ProviderID = await providers.CreateIfNotExists(x.Provider); + return x; + }))).ToList(); + } + + public async Task Delete(People obj) + { + _database.Peoples.Remove(obj); + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/ProviderRepository.cs b/Kyoo/Controllers/Repositories/ProviderRepository.cs new file mode 100644 index 00000000..4b304c45 --- /dev/null +++ b/Kyoo/Controllers/Repositories/ProviderRepository.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; + +namespace Kyoo.Controllers +{ + public class ProviderRepository : IProviderRepository + { + private readonly DatabaseContext _database; + + + public ProviderRepository(DatabaseContext database) + { + _database = database; + } + + public async Task Get(int id) + { + return await _database.Providers.FirstOrDefaultAsync(x => x.ID == id); + } + + public async Task Get(string slug) + { + return await _database.Providers.FirstOrDefaultAsync(x => x.Name == slug); + } + + public async Task> Search(string query) + { + return await _database.Providers + .Where(x => EF.Functions.Like(x.Name, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Providers.ToListAsync(); + } + + public async Task Create(ProviderID obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await _database.Providers.AddAsync(obj); + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(ProviderID obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + ProviderID old = await Get(obj.Name); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(ProviderID edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + ProviderID old = await Get(edited.Name); + + if (old == null) + throw new ItemNotFound($"No provider found with the name {edited.Name}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + await _database.SaveChangesAsync(); + } + + public async Task Delete(ProviderID obj) + { + _database.Providers.Remove(obj); + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs new file mode 100644 index 00000000..6e514871 --- /dev/null +++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Controllers +{ + public class SeasonRepository : ISeasonRepository + { + private readonly DatabaseContext _database; + private readonly IServiceProvider _serviceProvider; + + + public SeasonRepository(DatabaseContext database, IServiceProvider serviceProvider) + { + _database = database; + _serviceProvider = serviceProvider; + } + + public async Task Get(int id) + { + return await _database.Seasons.FirstOrDefaultAsync(x => x.ID == id); + } + + public Task Get(string slug) + { + int index = slug.IndexOf("-s", StringComparison.Ordinal); + if (index == -1) + throw new InvalidOperationException("Invalid season slug. Format: {showSlug}-s{seasonNumber}"); + string showSlug = slug.Substring(0, index); + if (!int.TryParse(slug.Substring(index + 2), out int seasonNumber)) + throw new InvalidOperationException("Invalid season slug. Format: {showSlug}-s{seasonNumber}"); + return Get(showSlug, seasonNumber); + } + + public async Task Get(string showSlug, int seasonNumber) + { + return await _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug + && x.SeasonNumber == seasonNumber); + } + + public async Task> Search(string query) + { + return await _database.Seasons + .Where(x => EF.Functions.Like(x.Title, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Seasons.ToListAsync(); + } + + public async Task Create(Season obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await Validate(obj); + _database.Entry(obj).State = EntityState.Added; + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Added; + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(Season obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Season old = await Get(obj.Slug); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(Season edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Season old = await Get(edited.Slug); + + if (old == null) + throw new ItemNotFound($"No season found with the slug {edited.Slug}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + + await Validate(old); + await _database.SaveChangesAsync(); + } + + private async Task Validate(Season obj) + { + if (obj.ShowID <= 0) + throw new InvalidOperationException($"Can't store a season not related to any show (showID: {obj.ShowID})."); + + if (obj.ExternalIDs != null) + { + obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x => + { + using IServiceScope serviceScope = _serviceProvider.CreateScope(); + IProviderRepository providers = serviceScope.ServiceProvider.GetService(); + + x.ProviderID = await providers.CreateIfNotExists(x.Provider); + return x; + }))).ToList(); + } + } + + public async Task Delete(Season obj) + { + _database.Seasons.Remove(obj); + await _database.SaveChangesAsync(); + } + + public async Task> GetSeasons(int showID) + { + return await _database.Seasons.Where(x => x.ShowID == showID).ToListAsync(); + } + + public async Task> GetSeasons(string showSlug) + { + return await _database.Seasons.Where(x => x.Show.Slug == showSlug).ToListAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs new file mode 100644 index 00000000..8d5a6772 --- /dev/null +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Controllers +{ + public class ShowRepository : IShowRepository + { + private readonly DatabaseContext _database; + private readonly IServiceProvider _serviceProvider; + private readonly IStudioRepository _studios; + + public ShowRepository(DatabaseContext database, + IServiceProvider serviceProvider, + IStudioRepository studios) + { + _database = database; + _serviceProvider = serviceProvider; + _studios = studios; + } + + public async Task Get(int id) + { + return await _database.Shows.FirstOrDefaultAsync(x => x.ID == id); + } + + public async Task Get(string slug) + { + return await _database.Shows.FirstOrDefaultAsync(x => x.Slug == slug); + } + + public async Task GetByPath(string path) + { + return await _database.Shows.FirstOrDefaultAsync(x => x.Path == path); + } + + public async Task> Search(string query) + { + return await _database.Shows + .FromSqlInterpolated($@"SELECT * FROM Shows WHERE Shows.Title LIKE {$"%{query}%"} + OR Shows.Aliases LIKE {$"%{query}%"}") + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Shows.ToListAsync(); + } + + public async Task Create(Show obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await Validate(obj); + _database.Entry(obj).State = EntityState.Added; + if (obj.GenreLinks != null) + foreach (GenreLink entry in obj.GenreLinks) + _database.Entry(entry).State = EntityState.Added; + if (obj.People != null) + foreach (PeopleLink entry in obj.People) + _database.Entry(entry).State = EntityState.Added; + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Added; + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(Show obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Show old = await Get(obj.Slug); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(Show edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Show old = await Get(edited.Slug); + + if (old == null) + throw new ItemNotFound($"No show found with the slug {edited.Slug}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + await Validate(old); + await _database.SaveChangesAsync(); + } + + private async Task Validate(Show obj) + { + if (obj.Studio != null) + obj.StudioID = await _studios.CreateIfNotExists(obj.Studio); + + if (obj.GenreLinks != null) + { + obj.GenreLinks = (await Task.WhenAll(obj.GenreLinks.Select(async x => + { + using IServiceScope serviceScope = _serviceProvider.CreateScope(); + IGenreRepository genres = serviceScope.ServiceProvider.GetService(); + + x.GenreID = await genres.CreateIfNotExists(x.Genre); + return x; + }))).ToList(); + } + + if (obj.People != null) + { + obj.People = (await Task.WhenAll(obj.People.Select(async x => + { + using IServiceScope serviceScope = _serviceProvider.CreateScope(); + IPeopleRepository people = serviceScope.ServiceProvider.GetService(); + + x.PeopleID = await people.CreateIfNotExists(x.People); + return x; + }))).ToList(); + } + + if (obj.ExternalIDs != null) + { + obj.ExternalIDs = (await Task.WhenAll(obj.ExternalIDs.Select(async x => + { + using IServiceScope serviceScope = _serviceProvider.CreateScope(); + IProviderRepository providers = serviceScope.ServiceProvider.GetService(); + + x.ProviderID = await providers.CreateIfNotExists(x.Provider); + return x; + }))).ToList(); + } + } + + public async Task Delete(Show show) + { + _database.Shows.Remove(show); + await _database.SaveChangesAsync(); + } + + public async Task AddShowLink(int showID, int? libraryID, int? collectionID) + { + if (collectionID != null) + { + _database.CollectionLinks.AddIfNotExist(new CollectionLink { CollectionID = collectionID, ShowID = showID}, + x => x.CollectionID == collectionID && x.ShowID == showID); + } + if (libraryID != null) + { + _database.LibraryLinks.AddIfNotExist(new LibraryLink {LibraryID = libraryID.Value, ShowID = showID}, + x => x.LibraryID == libraryID.Value && x.CollectionID == null && x.ShowID == showID); + } + + if (libraryID != null && collectionID != null) + { + _database.LibraryLinks.AddIfNotExist( + new LibraryLink {LibraryID = libraryID.Value, CollectionID = collectionID.Value}, + x => x.LibraryID == libraryID && x.CollectionID == collectionID && x.ShowID == null); + } + + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/StudioRepository.cs b/Kyoo/Controllers/Repositories/StudioRepository.cs new file mode 100644 index 00000000..a51f4ba5 --- /dev/null +++ b/Kyoo/Controllers/Repositories/StudioRepository.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; + +namespace Kyoo.Controllers +{ + public class StudioRepository : IStudioRepository + { + private readonly DatabaseContext _database; + + + public StudioRepository(DatabaseContext database) + { + _database = database; + } + + public async Task Get(int id) + { + return await _database.Studios.FirstOrDefaultAsync(x => x.ID == id); + } + + public async Task Get(string slug) + { + return await _database.Studios.FirstOrDefaultAsync(x => x.Name == slug); + } + + public async Task> Search(string query) + { + return await _database.Studios + .Where(x => EF.Functions.Like(x.Name, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public async Task> GetAll() + { + return await _database.Studios.ToListAsync(); + } + + public async Task Create(Studio obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + await _database.Studios.AddAsync(obj); + await _database.SaveChangesAsync(); + return obj.ID; + } + + public async Task CreateIfNotExists(Studio obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + Studio old = await Get(obj.Name); + if (old != null) + return old.ID; + return await Create(obj); + } + + public async Task Edit(Studio edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Studio old = await Get(edited.Name); + + if (old == null) + throw new ItemNotFound($"No studio found with the name {edited.Name}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + await _database.SaveChangesAsync(); + } + + public async Task Delete(Studio obj) + { + _database.Studios.Remove(obj); + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/TrackRepository.cs b/Kyoo/Controllers/Repositories/TrackRepository.cs new file mode 100644 index 00000000..6037889c --- /dev/null +++ b/Kyoo/Controllers/Repositories/TrackRepository.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; + +namespace Kyoo.Controllers +{ + public class TrackRepository : ITrackRepository + { + private readonly DatabaseContext _database; + + + public TrackRepository(DatabaseContext database) + { + _database = database; + } + + public async Task Get(int id) + { + return await _database.Tracks.FirstOrDefaultAsync(x => x.ID == id); + } + + public Task Get(string slug) + { + throw new InvalidOperationException("Tracks do not support the get by slug method."); + } + + public Task Get(int episodeID, string languageTag, bool isForced) + { + return _database.Tracks.FirstOrDefaultAsync(x => x.EpisodeID == episodeID + && x.Language == languageTag + && x.IsForced == isForced); + } + + public Task> Search(string query) + { + throw new InvalidOperationException("Tracks do not support the search method."); + } + + public async Task> GetAll() + { + return await _database.Tracks.ToListAsync(); + } + + public async Task Create(Track obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + if (obj.EpisodeID <= 0) + throw new InvalidOperationException($"Can't store a track not related to any episode (episodeID: {obj.EpisodeID})."); + + obj.Episode = null; + await _database.Tracks.AddAsync(obj); + await _database.SaveChangesAsync(); + return obj.ID; + } + + public Task CreateIfNotExists(Track obj) + { + return Create(obj); + } + + public async Task Edit(Track edited, bool resetOld) + { + if (edited == null) + throw new ArgumentNullException(nameof(edited)); + + Track old = await Get(edited.ID); + + if (old == null) + throw new ItemNotFound($"No track found with the ID {edited.ID}."); + + if (resetOld) + Utility.Nullify(old); + Utility.Merge(old, edited); + await _database.SaveChangesAsync(); + } + + public async Task Delete(Track obj) + { + _database.Tracks.Remove(obj); + await _database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/Kyoo/Models/DatabaseMigrations/Internal/20200526235513_Initial.Designer.cs b/Kyoo/Models/DatabaseMigrations/Internal/20200607010830_Initial.Designer.cs similarity index 80% rename from Kyoo/Models/DatabaseMigrations/Internal/20200526235513_Initial.Designer.cs rename to Kyoo/Models/DatabaseMigrations/Internal/20200607010830_Initial.Designer.cs index 559076f9..1aaddf9b 100644 --- a/Kyoo/Models/DatabaseMigrations/Internal/20200526235513_Initial.Designer.cs +++ b/Kyoo/Models/DatabaseMigrations/Internal/20200607010830_Initial.Designer.cs @@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Kyoo.Models.DatabaseMigrations.Internal { [DbContext(typeof(DatabaseContext))] - [Migration("20200526235513_Initial")] + [Migration("20200607010830_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -23,9 +23,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Collection", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("ImgPrimary") @@ -53,16 +53,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.CollectionLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("CollectionID") - .HasColumnType("bigint"); + b.Property("CollectionID") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -75,16 +75,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Episode", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("AbsoluteNumber") - .HasColumnType("bigint"); + b.Property("AbsoluteNumber") + .HasColumnType("integer"); - b.Property("EpisodeNumber") - .HasColumnType("bigint"); + b.Property("EpisodeNumber") + .HasColumnType("integer"); b.Property("ImgPrimary") .HasColumnType("text"); @@ -98,17 +98,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("ReleaseDate") .HasColumnType("timestamp without time zone"); - b.Property("Runtime") - .HasColumnType("bigint"); + b.Property("Runtime") + .HasColumnType("integer"); - b.Property("SeasonID") - .HasColumnType("bigint"); + b.Property("SeasonID") + .HasColumnType("integer"); - b.Property("SeasonNumber") - .HasColumnType("bigint"); + b.Property("SeasonNumber") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.Property("Title") .HasColumnType("text"); @@ -124,9 +124,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Genre", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Name") @@ -145,11 +145,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.GenreLink", b => { - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); - b.Property("GenreID") - .HasColumnType("bigint"); + b.Property("GenreID") + .HasColumnType("integer"); b.HasKey("ShowID", "GenreID"); @@ -160,9 +160,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Library", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Name") @@ -184,19 +184,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.LibraryLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("CollectionID") - .HasColumnType("bigint"); + b.Property("CollectionID") + .HasColumnType("integer"); - b.Property("LibraryID") - .HasColumnType("bigint"); + b.Property("LibraryID") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -211,31 +211,31 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.MetadataID", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("DataID") .HasColumnType("text"); - b.Property("EpisodeID") - .HasColumnType("bigint"); + b.Property("EpisodeID") + .HasColumnType("integer"); b.Property("Link") .HasColumnType("text"); - b.Property("PeopleID") - .HasColumnType("bigint"); + b.Property("PeopleID") + .HasColumnType("integer"); - b.Property("ProviderID") - .HasColumnType("bigint"); + b.Property("ProviderID") + .HasColumnType("integer"); - b.Property("SeasonID") - .HasColumnType("bigint"); + b.Property("SeasonID") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -254,9 +254,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.People", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("ImgPrimary") @@ -278,19 +278,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.PeopleLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("PeopleID") - .HasColumnType("bigint"); + b.Property("PeopleID") + .HasColumnType("integer"); b.Property("Role") .HasColumnType("text"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.Property("Type") .HasColumnType("text"); @@ -306,9 +306,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.ProviderID", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Logo") @@ -327,16 +327,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.ProviderLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("LibraryID") - .HasColumnType("bigint"); + b.Property("LibraryID") + .HasColumnType("integer"); - b.Property("ProviderID") - .HasColumnType("bigint"); + b.Property("ProviderID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -349,9 +349,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Season", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("ImgPrimary") @@ -360,17 +360,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("Overview") .HasColumnType("text"); - b.Property("SeasonNumber") - .HasColumnType("bigint"); + b.Property("SeasonNumber") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.Property("Title") .HasColumnType("text"); - b.Property("Year") - .HasColumnType("bigint"); + b.Property("Year") + .HasColumnType("integer"); b.HasKey("ID"); @@ -381,9 +381,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Show", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Aliases") @@ -392,8 +392,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("Backdrop") .HasColumnType("text"); - b.Property("EndYear") - .HasColumnType("bigint"); + b.Property("EndYear") + .HasColumnType("integer"); b.Property("IsMovie") .HasColumnType("boolean"); @@ -413,14 +413,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("Slug") .HasColumnType("text"); - b.Property("StartYear") - .HasColumnType("bigint"); + b.Property("StartYear") + .HasColumnType("integer"); b.Property("Status") .HasColumnType("integer"); - b.Property("StudioID") - .HasColumnType("bigint"); + b.Property("StudioID") + .HasColumnType("integer"); b.Property("Title") .HasColumnType("text"); @@ -440,9 +440,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Studio", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Name") @@ -461,16 +461,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Track", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Codec") .HasColumnType("text"); - b.Property("EpisodeID") - .HasColumnType("bigint"); + b.Property("EpisodeID") + .HasColumnType("integer"); b.Property("IsDefault") .HasColumnType("boolean"); diff --git a/Kyoo/Models/DatabaseMigrations/Internal/20200526235513_Initial.cs b/Kyoo/Models/DatabaseMigrations/Internal/20200607010830_Initial.cs similarity index 88% rename from Kyoo/Models/DatabaseMigrations/Internal/20200526235513_Initial.cs rename to Kyoo/Models/DatabaseMigrations/Internal/20200607010830_Initial.cs index d3b90b3a..f83d1361 100644 --- a/Kyoo/Models/DatabaseMigrations/Internal/20200526235513_Initial.cs +++ b/Kyoo/Models/DatabaseMigrations/Internal/20200607010830_Initial.cs @@ -12,7 +12,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Collections", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Slug = table.Column(nullable: true), Name = table.Column(nullable: true), @@ -29,7 +29,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Genres", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Slug = table.Column(nullable: true), Name = table.Column(nullable: true) @@ -43,7 +43,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Libraries", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Slug = table.Column(nullable: true), Name = table.Column(nullable: true), @@ -58,7 +58,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Peoples", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Slug = table.Column(nullable: true), Name = table.Column(nullable: true), @@ -73,7 +73,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Providers", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Name = table.Column(nullable: true), Logo = table.Column(nullable: true) @@ -87,7 +87,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Studios", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Slug = table.Column(nullable: true), Name = table.Column(nullable: true) @@ -101,10 +101,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "ProviderLinks", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ProviderID = table.Column(nullable: false), - LibraryID = table.Column(nullable: true) + ProviderID = table.Column(nullable: false), + LibraryID = table.Column(nullable: true) }, constraints: table => { @@ -127,7 +127,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Shows", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Slug = table.Column(nullable: true), Title = table.Column(nullable: true), @@ -136,13 +136,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal Overview = table.Column(nullable: true), Status = table.Column(nullable: true), TrailerUrl = table.Column(nullable: true), - StartYear = table.Column(nullable: true), - EndYear = table.Column(nullable: true), + StartYear = table.Column(nullable: true), + EndYear = table.Column(nullable: true), Poster = table.Column(nullable: true), Logo = table.Column(nullable: true), Backdrop = table.Column(nullable: true), IsMovie = table.Column(nullable: false), - StudioID = table.Column(nullable: true) + StudioID = table.Column(nullable: true) }, constraints: table => { @@ -159,10 +159,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "CollectionLinks", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - CollectionID = table.Column(nullable: true), - ShowID = table.Column(nullable: false) + CollectionID = table.Column(nullable: true), + ShowID = table.Column(nullable: false) }, constraints: table => { @@ -185,8 +185,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "GenreLinks", columns: table => new { - ShowID = table.Column(nullable: false), - GenreID = table.Column(nullable: false) + ShowID = table.Column(nullable: false), + GenreID = table.Column(nullable: false) }, constraints: table => { @@ -209,11 +209,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "LibraryLinks", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - LibraryID = table.Column(nullable: false), - ShowID = table.Column(nullable: true), - CollectionID = table.Column(nullable: true) + LibraryID = table.Column(nullable: false), + ShowID = table.Column(nullable: true), + CollectionID = table.Column(nullable: true) }, constraints: table => { @@ -242,10 +242,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "PeopleLinks", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - PeopleID = table.Column(nullable: false), - ShowID = table.Column(nullable: false), + PeopleID = table.Column(nullable: false), + ShowID = table.Column(nullable: false), Role = table.Column(nullable: true), Type = table.Column(nullable: true) }, @@ -270,13 +270,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Seasons", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ShowID = table.Column(nullable: false), - SeasonNumber = table.Column(nullable: false), + ShowID = table.Column(nullable: false), + SeasonNumber = table.Column(nullable: false), Title = table.Column(nullable: true), Overview = table.Column(nullable: true), - Year = table.Column(nullable: true), + Year = table.Column(nullable: true), ImgPrimary = table.Column(nullable: true) }, constraints: table => @@ -294,18 +294,18 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Episodes", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ShowID = table.Column(nullable: false), - SeasonID = table.Column(nullable: true), - SeasonNumber = table.Column(nullable: false), - EpisodeNumber = table.Column(nullable: false), - AbsoluteNumber = table.Column(nullable: false), + ShowID = table.Column(nullable: false), + SeasonID = table.Column(nullable: true), + SeasonNumber = table.Column(nullable: false), + EpisodeNumber = table.Column(nullable: false), + AbsoluteNumber = table.Column(nullable: false), Path = table.Column(nullable: true), Title = table.Column(nullable: true), Overview = table.Column(nullable: true), ReleaseDate = table.Column(nullable: true), - Runtime = table.Column(nullable: false), + Runtime = table.Column(nullable: false), ImgPrimary = table.Column(nullable: true) }, constraints: table => @@ -329,13 +329,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "MetadataIds", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ProviderID = table.Column(nullable: false), - ShowID = table.Column(nullable: true), - EpisodeID = table.Column(nullable: true), - SeasonID = table.Column(nullable: true), - PeopleID = table.Column(nullable: true), + ProviderID = table.Column(nullable: false), + ShowID = table.Column(nullable: true), + EpisodeID = table.Column(nullable: true), + SeasonID = table.Column(nullable: true), + PeopleID = table.Column(nullable: true), DataID = table.Column(nullable: true), Link = table.Column(nullable: true) }, @@ -378,14 +378,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal name: "Tracks", columns: table => new { - ID = table.Column(nullable: false) + ID = table.Column(nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Title = table.Column(nullable: true), Language = table.Column(nullable: true), Codec = table.Column(nullable: true), Path = table.Column(nullable: true), Type = table.Column(nullable: false), - EpisodeID = table.Column(nullable: false), + EpisodeID = table.Column(nullable: false), IsDefault = table.Column(nullable: false), IsForced = table.Column(nullable: false), IsExternal = table.Column(nullable: false) diff --git a/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs b/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs index be3dd87f..a0e42322 100644 --- a/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs +++ b/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs @@ -21,9 +21,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Collection", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("ImgPrimary") @@ -51,16 +51,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.CollectionLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("CollectionID") - .HasColumnType("bigint"); + b.Property("CollectionID") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -73,16 +73,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Episode", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("AbsoluteNumber") - .HasColumnType("bigint"); + b.Property("AbsoluteNumber") + .HasColumnType("integer"); - b.Property("EpisodeNumber") - .HasColumnType("bigint"); + b.Property("EpisodeNumber") + .HasColumnType("integer"); b.Property("ImgPrimary") .HasColumnType("text"); @@ -96,17 +96,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("ReleaseDate") .HasColumnType("timestamp without time zone"); - b.Property("Runtime") - .HasColumnType("bigint"); + b.Property("Runtime") + .HasColumnType("integer"); - b.Property("SeasonID") - .HasColumnType("bigint"); + b.Property("SeasonID") + .HasColumnType("integer"); - b.Property("SeasonNumber") - .HasColumnType("bigint"); + b.Property("SeasonNumber") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.Property("Title") .HasColumnType("text"); @@ -122,9 +122,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Genre", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Name") @@ -143,11 +143,11 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.GenreLink", b => { - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); - b.Property("GenreID") - .HasColumnType("bigint"); + b.Property("GenreID") + .HasColumnType("integer"); b.HasKey("ShowID", "GenreID"); @@ -158,9 +158,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Library", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Name") @@ -182,19 +182,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.LibraryLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("CollectionID") - .HasColumnType("bigint"); + b.Property("CollectionID") + .HasColumnType("integer"); - b.Property("LibraryID") - .HasColumnType("bigint"); + b.Property("LibraryID") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -209,31 +209,31 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.MetadataID", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("DataID") .HasColumnType("text"); - b.Property("EpisodeID") - .HasColumnType("bigint"); + b.Property("EpisodeID") + .HasColumnType("integer"); b.Property("Link") .HasColumnType("text"); - b.Property("PeopleID") - .HasColumnType("bigint"); + b.Property("PeopleID") + .HasColumnType("integer"); - b.Property("ProviderID") - .HasColumnType("bigint"); + b.Property("ProviderID") + .HasColumnType("integer"); - b.Property("SeasonID") - .HasColumnType("bigint"); + b.Property("SeasonID") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -252,9 +252,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.People", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("ImgPrimary") @@ -276,19 +276,19 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.PeopleLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("PeopleID") - .HasColumnType("bigint"); + b.Property("PeopleID") + .HasColumnType("integer"); b.Property("Role") .HasColumnType("text"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.Property("Type") .HasColumnType("text"); @@ -304,9 +304,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.ProviderID", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Logo") @@ -325,16 +325,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.ProviderLink", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - b.Property("LibraryID") - .HasColumnType("bigint"); + b.Property("LibraryID") + .HasColumnType("integer"); - b.Property("ProviderID") - .HasColumnType("bigint"); + b.Property("ProviderID") + .HasColumnType("integer"); b.HasKey("ID"); @@ -347,9 +347,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Season", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("ImgPrimary") @@ -358,17 +358,17 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("Overview") .HasColumnType("text"); - b.Property("SeasonNumber") - .HasColumnType("bigint"); + b.Property("SeasonNumber") + .HasColumnType("integer"); - b.Property("ShowID") - .HasColumnType("bigint"); + b.Property("ShowID") + .HasColumnType("integer"); b.Property("Title") .HasColumnType("text"); - b.Property("Year") - .HasColumnType("bigint"); + b.Property("Year") + .HasColumnType("integer"); b.HasKey("ID"); @@ -379,9 +379,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Show", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Aliases") @@ -390,8 +390,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("Backdrop") .HasColumnType("text"); - b.Property("EndYear") - .HasColumnType("bigint"); + b.Property("EndYear") + .HasColumnType("integer"); b.Property("IsMovie") .HasColumnType("boolean"); @@ -411,14 +411,14 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.Property("Slug") .HasColumnType("text"); - b.Property("StartYear") - .HasColumnType("bigint"); + b.Property("StartYear") + .HasColumnType("integer"); b.Property("Status") .HasColumnType("integer"); - b.Property("StudioID") - .HasColumnType("bigint"); + b.Property("StudioID") + .HasColumnType("integer"); b.Property("Title") .HasColumnType("text"); @@ -438,9 +438,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Studio", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Name") @@ -459,16 +459,16 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.Track", b => { - b.Property("ID") + b.Property("ID") .ValueGeneratedOnAdd() - .HasColumnType("bigint") + .HasColumnType("integer") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property("Codec") .HasColumnType("text"); - b.Property("EpisodeID") - .HasColumnType("bigint"); + b.Property("EpisodeID") + .HasColumnType("integer"); b.Property("IsDefault") .HasColumnType("boolean"); diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index 22baaf05..bd1a5272 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -137,6 +137,18 @@ namespace Kyoo AllowedOrigins = { new Uri(publicUrl).GetLeftPart(UriPartial.Authority) } }); + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddScoped(); services.AddSingleton(); services.AddSingleton(); diff --git a/Kyoo/Tasks/Crawler.cs b/Kyoo/Tasks/Crawler.cs index 065bc613..3a5c8ae9 100644 --- a/Kyoo/Tasks/Crawler.cs +++ b/Kyoo/Tasks/Crawler.cs @@ -8,7 +8,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Kyoo.Models.Watch; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace Kyoo.Controllers @@ -28,11 +27,11 @@ namespace Kyoo.Controllers private ITranscoder _transcoder; private IConfiguration _config; - public IEnumerable GetPossibleParameters() + public async Task> GetPossibleParameters() { using IServiceScope serviceScope = _serviceProvider.CreateScope(); ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService(); - return libraryManager.GetLibraries().Select(x => x.Slug); + return (await libraryManager.GetLibraries()).Select(x => x.Slug); } public int? Progress() @@ -53,28 +52,27 @@ namespace Kyoo.Controllers { using IServiceScope serviceScope = _serviceProvider.CreateScope(); ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService(); - IEnumerable episodes = libraryManager.GetEpisodes(); - IEnumerable libraries = argument == null - ? libraryManager.GetLibraries() - : new [] {libraryManager.GetLibrary(argument)}; + ICollection episodes = await libraryManager.GetEpisodes(); + ICollection libraries = argument == null + ? await libraryManager.GetLibraries() + : new [] { await libraryManager.GetLibrary(argument)}; foreach (Episode episode in episodes) { if (!File.Exists(episode.Path)) - libraryManager.RemoveEpisode(episode); + await libraryManager.DeleteEpisode(episode); } - await libraryManager.SaveChanges(); - await Task.WhenAll(libraries.ToList().Select(x => Scan(x, libraryManager, cancellationToken))); + await Task.WhenAll(libraries.Select(x => Scan(x, episodes, cancellationToken))); } catch (Exception ex) { - Console.Error.WriteLine($"Unknown exception thrown durring libraries scan.\nException: {ex.Message}"); + await Console.Error.WriteLineAsync($"Unknown exception thrown durring libraries scan.\nException: {ex.Message}"); } Console.WriteLine("Scan finished!"); } - private Task Scan(Library library, ILibraryManager libraryManager, CancellationToken cancellationToken) + private Task Scan(Library library, ICollection episodes, CancellationToken cancellationToken) { Console.WriteLine($"Scanning library {library.Name} at {string.Join(", ", library.Paths)}."); return Task.WhenAll(library.Paths.Select(async path => @@ -89,29 +87,29 @@ namespace Kyoo.Controllers } catch (DirectoryNotFoundException) { - Console.Error.WriteLine($"The library's directory {path} could not be found (library slug: {library.Slug})"); + await Console.Error.WriteLineAsync($"The library's directory {path} could not be found (library slug: {library.Slug})"); return Task.CompletedTask; } catch (PathTooLongException) { - Console.Error.WriteLine($"The library's directory {path} is too long for this system. (library slug: {library.Slug})"); + await Console.Error.WriteLineAsync($"The library's directory {path} is too long for this system. (library slug: {library.Slug})"); return Task.CompletedTask; } catch (ArgumentException) { - Console.Error.WriteLine($"The library's directory {path} is invalid. (library slug: {library.Slug})"); + await Console.Error.WriteLineAsync($"The library's directory {path} is invalid. (library slug: {library.Slug})"); return Task.CompletedTask; } catch (UnauthorizedAccessException) { - Console.Error.WriteLine($"Permission denied: can't access library's directory at {path}. (library slug: {library.Slug})"); + await Console.Error.WriteLineAsync($"Permission denied: can't access library's directory at {path}. (library slug: {library.Slug})"); return Task.CompletedTask; } // return Task.WhenAll(files.Select(file => foreach (string file in files) { - if (!IsVideo(file) || libraryManager.GetEpisodes().Any(x => x.Path == file)) + if (!IsVideo(file) || episodes.Any(x => x.Path == file)) continue; //return Task.CompletedTask; string relativePath = file.Substring(path.Length); /*return*/ await RegisterFile(file, relativePath, library, cancellationToken); @@ -128,7 +126,6 @@ namespace Kyoo.Controllers using IServiceScope serviceScope = _serviceProvider.CreateScope(); ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService(); - ((DbSet)libraryManager.GetLibraries()).Attach(library); string patern = _config.GetValue("regex"); Regex regex = new Regex(patern, RegexOptions.IgnoreCase); @@ -137,74 +134,97 @@ namespace Kyoo.Controllers string showPath = Path.GetDirectoryName(path); string collectionName = match.Groups["Collection"]?.Value; string showName = match.Groups["ShowTitle"].Value; - long seasonNumber = long.TryParse(match.Groups["Season"].Value, out long tmp) ? tmp : -1; - long episodeNumber = long.TryParse(match.Groups["Episode"].Value, out tmp) ? tmp : -1; - long absoluteNumber = long.TryParse(match.Groups["Absolute"].Value, out tmp) ? tmp : -1; + int seasonNumber = int.TryParse(match.Groups["Season"].Value, out int tmp) ? tmp : -1; + int episodeNumber = int.TryParse(match.Groups["Episode"].Value, out tmp) ? tmp : -1; + int absoluteNumber = int.TryParse(match.Groups["Absolute"].Value, out tmp) ? tmp : -1; Collection collection = await GetCollection(libraryManager, collectionName, library); bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1; Show show = await GetShow(libraryManager, showName, showPath, isMovie, library); if (isMovie) - libraryManager.Register(await GetMovie(show, path)); + await libraryManager.RegisterEpisode(await GetMovie(show, path)); else { Season season = await GetSeason(libraryManager, show, seasonNumber, library); Episode episode = await GetEpisode(libraryManager, show, season, episodeNumber, absoluteNumber, path, library); - libraryManager.Register(episode); + await libraryManager.RegisterEpisode(episode); } - if (collection != null) - libraryManager.Register(collection); - libraryManager.RegisterShowLinks(library, collection, show); - Console.WriteLine($"Registering episode at: {path}"); - await libraryManager.SaveChanges(); + + await libraryManager.AddShowLink(show, library, collection); + Console.WriteLine($"Episode at {path} registered."); } - private async Task GetCollection(ILibraryManager libraryManager, string collectionName, Library library) + private async Task GetCollection(ILibraryManager libraryManager, + string collectionName, + Library library) { if (string.IsNullOrEmpty(collectionName)) - return await Task.FromResult(null); - Collection name = libraryManager.GetCollection(Utility.ToSlug(collectionName)); - if (name != null) - return name; - return await _metadataProvider.GetCollectionFromName(collectionName, library); + return null; + Collection collection = await libraryManager.GetCollection(Utility.ToSlug(collectionName)); + if (collection != null) + return collection; + collection = await _metadataProvider.GetCollectionFromName(collectionName, library); + await libraryManager.RegisterCollection(collection); + return collection; } - private async Task GetShow(ILibraryManager libraryManager, string showTitle, string showPath, bool isMovie, Library library) + private async Task GetShow(ILibraryManager libraryManager, + string showTitle, + string showPath, + bool isMovie, + Library library) { - Show show = libraryManager.GetShowByPath(showPath); + Show show = await libraryManager.GetShowByPath(showPath); if (show != null) return show; show = await _metadataProvider.SearchShow(showTitle, isMovie, library); show.Path = showPath; show.People = await _metadataProvider.GetPeople(show, library); + await libraryManager.RegisterShow(show); await _thumbnailsManager.Validate(show.People); await _thumbnailsManager.Validate(show); return show; } - private async Task GetSeason(ILibraryManager libraryManager, Show show, long seasonNumber, Library library) + private async Task GetSeason(ILibraryManager libraryManager, + Show show, + int seasonNumber, + Library library) { if (seasonNumber == -1) return default; - Season season = libraryManager.GetSeason(show.Slug, seasonNumber); + Season season = await libraryManager.GetSeason(show.Slug, seasonNumber); if (season == null) { season = await _metadataProvider.GetSeason(show, seasonNumber, library); + await libraryManager.RegisterSeason(season); await _thumbnailsManager.Validate(season); } season.Show = show; return season; } - private async Task GetEpisode(ILibraryManager libraryManager, Show show, Season season, long episodeNumber, long absoluteNumber, string episodePath, Library library) + private async Task GetEpisode(ILibraryManager libraryManager, + Show show, + Season season, + int episodeNumber, + int absoluteNumber, + string episodePath, + Library library) { - Episode episode = await _metadataProvider.GetEpisode(show, episodePath, season?.SeasonNumber ?? -1, episodeNumber, absoluteNumber, library); - if (season == null) - season = await GetSeason(libraryManager, show, episode.SeasonNumber, library); + Episode episode = await _metadataProvider.GetEpisode(show, + episodePath, + season?.SeasonNumber ?? -1, + episodeNumber, + absoluteNumber, + library); + + season ??= await GetSeason(libraryManager, show, episode.SeasonNumber, library); episode.Season = season; + episode.SeasonID = season?.ID; if (season == null) { - await Console.Error.WriteLineAsync("\tError: You don't have any provider that support absolute epiode numbering. Install one and try again."); + await Console.Error.WriteLineAsync("Error: You don't have any provider that support absolute epiode numbering. Install one and try again."); return default; } @@ -215,7 +235,13 @@ namespace Kyoo.Controllers private async Task GetMovie(Show show, string episodePath) { - Episode episode = new Episode {Title = show.Title, Path = episodePath, Show = show}; + Episode episode = new Episode + { + Title = show.Title, + Path = episodePath, + Show = show, + ShowID = show.ID + }; episode.Tracks = await GetTracks(episode); return episode; } diff --git a/Kyoo/Tasks/CreateDatabase.cs b/Kyoo/Tasks/CreateDatabase.cs index c5345ab4..d4e751dc 100644 --- a/Kyoo/Tasks/CreateDatabase.cs +++ b/Kyoo/Tasks/CreateDatabase.cs @@ -58,9 +58,9 @@ namespace Kyoo.Tasks return Task.CompletedTask; } - public IEnumerable GetPossibleParameters() + public Task> GetPossibleParameters() { - return null; + return Task.FromResult>(null); } public int? Progress() diff --git a/Kyoo/Tasks/MetadataLoader.cs b/Kyoo/Tasks/MetadataLoader.cs index 427eb22e..c66702c5 100644 --- a/Kyoo/Tasks/MetadataLoader.cs +++ b/Kyoo/Tasks/MetadataLoader.cs @@ -28,9 +28,9 @@ namespace Kyoo.Tasks return Task.CompletedTask; } - public IEnumerable GetPossibleParameters() + public Task> GetPossibleParameters() { - return null; + return Task.FromResult>(null); } public int? Progress() diff --git a/Kyoo/Tasks/PluginLoader.cs b/Kyoo/Tasks/PluginLoader.cs index a174caa5..b3f1c064 100644 --- a/Kyoo/Tasks/PluginLoader.cs +++ b/Kyoo/Tasks/PluginLoader.cs @@ -24,9 +24,9 @@ namespace Kyoo.Tasks return Task.CompletedTask; } - public IEnumerable GetPossibleParameters() + public Task> GetPossibleParameters() { - return null; + return Task.FromResult>(null); } public int? Progress() diff --git a/Kyoo/Tasks/ReScan.cs b/Kyoo/Tasks/ReScan.cs index 22c7aeb5..7ab182a7 100644 --- a/Kyoo/Tasks/ReScan.cs +++ b/Kyoo/Tasks/ReScan.cs @@ -62,7 +62,7 @@ namespace Kyoo.Tasks edited.ID = old.ID; edited.Slug = old.Slug; edited.Path = old.Path; - await libraryManager.Edit(edited, true); + await libraryManager.EditShow(edited, true); await _thumbnailsManager.Validate(edited, true); } if (old.Seasons != null) @@ -95,7 +95,7 @@ namespace Kyoo.Tasks Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library; Season edited = await _providerManager.GetSeason(show, old.SeasonNumber, library); edited.ID = old.ID; - await libraryManager.Edit(edited, true); + await libraryManager.EditSeason(edited, true); await _thumbnailsManager.Validate(edited, true); } if (old.Episodes != null) @@ -110,13 +110,13 @@ namespace Kyoo.Tasks Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library; Episode edited = await _providerManager.GetEpisode(show, old.Path, old.SeasonNumber, old.EpisodeNumber, old.AbsoluteNumber, library); edited.ID = old.ID; - await libraryManager.Edit(edited, true); + await libraryManager.EditEpisode(edited, true); await _thumbnailsManager.Validate(edited, true); } - public IEnumerable GetPossibleParameters() + public Task> GetPossibleParameters() { - return default; + return Task.FromResult>(null); } public int? Progress() diff --git a/Kyoo/Views/API/CollectionAPI.cs b/Kyoo/Views/API/CollectionAPI.cs index f15ff86d..bd4658fa 100644 --- a/Kyoo/Views/API/CollectionAPI.cs +++ b/Kyoo/Views/API/CollectionAPI.cs @@ -2,6 +2,7 @@ using Kyoo.Models; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; namespace Kyoo.Api @@ -19,9 +20,9 @@ namespace Kyoo.Api [HttpGet("{collectionSlug}")] [Authorize(Policy="Read")] - public ActionResult GetShows(string collectionSlug) + public async Task> GetShows(string collectionSlug) { - Collection collection = _libraryManager.GetCollection(collectionSlug); + Collection collection = await _libraryManager.GetCollection(collectionSlug); if (collection == null) return NotFound(); diff --git a/Kyoo/Views/API/EpisodesAPI.cs b/Kyoo/Views/API/EpisodesAPI.cs index d4fe7bd0..abf0650d 100644 --- a/Kyoo/Views/API/EpisodesAPI.cs +++ b/Kyoo/Views/API/EpisodesAPI.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Kyoo.Controllers; using Microsoft.AspNetCore.Authorization; @@ -20,9 +21,9 @@ namespace Kyoo.Api [HttpGet("{showSlug}/season/{seasonNumber}")] [Authorize(Policy="Read")] - public ActionResult> GetEpisodesForSeason(string showSlug, long seasonNumber) + public async Task>> GetEpisodesForSeason(string showSlug, int seasonNumber) { - IEnumerable episodes = _libraryManager.GetEpisodes(showSlug, seasonNumber); + IEnumerable episodes = await _libraryManager.GetEpisodes(showSlug, seasonNumber); if(episodes == null) return NotFound(); @@ -33,9 +34,9 @@ namespace Kyoo.Api [HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")] [Authorize(Policy="Read")] [JsonDetailed] - public ActionResult GetEpisode(string showSlug, long seasonNumber, long episodeNumber) + public async Task> GetEpisode(string showSlug, int seasonNumber, int episodeNumber) { - Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); + Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); if (episode == null) return NotFound(); diff --git a/Kyoo/Views/API/GenresAPI.cs b/Kyoo/Views/API/GenresAPI.cs index 21dc5643..98d27e7f 100644 --- a/Kyoo/Views/API/GenresAPI.cs +++ b/Kyoo/Views/API/GenresAPI.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Kyoo.Controllers; using Kyoo.Models; using Microsoft.AspNetCore.Mvc; @@ -18,9 +19,9 @@ namespace Kyoo.API _libraryManager = libraryManager; } - public ActionResult> Index() + public async Task>> Index() { - return _libraryManager.GetGenres().ToList(); + return (await _libraryManager.GetGenres()).ToList(); } } } \ No newline at end of file diff --git a/Kyoo/Views/API/LibrariesAPI.cs b/Kyoo/Views/API/LibrariesAPI.cs index 32aa73f6..eeeee468 100644 --- a/Kyoo/Views/API/LibrariesAPI.cs +++ b/Kyoo/Views/API/LibrariesAPI.cs @@ -3,6 +3,7 @@ using Kyoo.Models; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; namespace Kyoo.Api @@ -22,15 +23,15 @@ namespace Kyoo.Api } [HttpGet] - public IEnumerable GetLibraries() + public async Task> GetLibraries() { - return _libraryManager.GetLibraries(); + return await _libraryManager.GetLibraries(); } [Route("/api/library/create")] [HttpPost] [Authorize(Policy="Admin")] - public IActionResult CreateLibrary([FromBody] Library library) + public async Task CreateLibrary([FromBody] Library library) { if (!ModelState.IsValid) return BadRequest(library); @@ -40,19 +41,18 @@ namespace Kyoo.Api return BadRequest(new {error = "The library's name must be set and not empty"}); if (library.Paths == null || !library.Paths.Any()) return BadRequest(new {error = "The library should have a least one path."}); - if (_libraryManager.GetLibrary(library.Slug) != null) + if (await _libraryManager.GetLibrary(library.Slug) != null) return BadRequest(new {error = "Duplicated library slug"}); - _libraryManager.Register(library); - _libraryManager.SaveChanges(); + await _libraryManager.RegisterLibrary(library); _taskManager.StartTask("scan", library.Slug); return Ok(); } [HttpGet("{librarySlug}")] [Authorize(Policy="Read")] - public ActionResult> GetShows(string librarySlug) + public async Task>> GetShows(string librarySlug) { - Library library = _libraryManager.GetLibrary(librarySlug); + Library library = await _libraryManager.GetLibrary(librarySlug); if (library == null) return NotFound(); diff --git a/Kyoo/Views/API/PeopleAPI.cs b/Kyoo/Views/API/PeopleAPI.cs index ca21eb0b..ad27d8b9 100644 --- a/Kyoo/Views/API/PeopleAPI.cs +++ b/Kyoo/Views/API/PeopleAPI.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using Kyoo.Controllers; using Kyoo.Models; using Microsoft.AspNetCore.Authorization; @@ -19,9 +20,9 @@ namespace Kyoo.Api [HttpGet("{peopleSlug}")] [Authorize(Policy="Read")] - public ActionResult GetPeople(string peopleSlug) + public async Task> GetPeople(string peopleSlug) { - People people = _libraryManager.GetPeople(peopleSlug); + People people = await _libraryManager.GetPeople(peopleSlug); if (people == null) return NotFound(); diff --git a/Kyoo/Views/API/SearchAPI.cs b/Kyoo/Views/API/SearchAPI.cs index ab0ab3dd..4efcf93a 100644 --- a/Kyoo/Views/API/SearchAPI.cs +++ b/Kyoo/Views/API/SearchAPI.cs @@ -1,4 +1,5 @@ -using Kyoo.Controllers; +using System.Threading.Tasks; +using Kyoo.Controllers; using Kyoo.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -18,17 +19,17 @@ namespace Kyoo.Api [HttpGet("{query}")] [Authorize(Policy="Read")] - public ActionResult Search(string query) + public async Task> Search(string query) { SearchResult result = new SearchResult { Query = query, - Collections = _libraryManager.SearchCollections(query), - Shows = _libraryManager.SearchShows(query), - Episodes = _libraryManager.SearchEpisodes(query), - People = _libraryManager.SearchPeople(query), - Genres = _libraryManager.SearchGenres(query), - Studios = _libraryManager.SearchStudios(query) + Collections = await _libraryManager.SearchCollections(query), + Shows = await _libraryManager.SearchShows(query), + Episodes = await _libraryManager.SearchEpisodes(query), + People = await _libraryManager.SearchPeople(query), + Genres = await _libraryManager.SearchGenres(query), + Studios = await _libraryManager.SearchStudios(query) }; return result; } diff --git a/Kyoo/Views/API/ShowsAPI.cs b/Kyoo/Views/API/ShowsAPI.cs index d656130c..a22a485e 100644 --- a/Kyoo/Views/API/ShowsAPI.cs +++ b/Kyoo/Views/API/ShowsAPI.cs @@ -37,15 +37,18 @@ namespace Kyoo.Api [Authorize(Policy="Read")] public IEnumerable GetShows() { - return _database.LibraryLinks.AsEnumerable().Select(x => x.Show ?? x.Collection.AsShow()); + return _database.LibraryLinks + .Include(x => x.Show) + .Include(x => x.Collection) + .AsEnumerable().Select(x => x.Show ?? x.Collection.AsShow()).ToList(); } [HttpGet("{slug}")] [Authorize(Policy="Read")] [JsonDetailed] - public ActionResult GetShow(string slug) + public async Task> GetShow(string slug) { - Show show = _libraryManager.GetShow(slug); + Show show = await _libraryManager.GetShow(slug); if (show == null) return NotFound(); @@ -55,7 +58,7 @@ namespace Kyoo.Api [HttpPost("edit/{slug}")] [Authorize(Policy="Write")] - public IActionResult EditShow(string slug, [FromBody] Show show) + public async Task EditShow(string slug, [FromBody] Show show) { if (!ModelState.IsValid) return BadRequest(show); @@ -66,7 +69,7 @@ namespace Kyoo.Api show.ID = old.ID; show.Slug = slug; show.Path = old.Path; - _libraryManager.Edit(show, false); + await _libraryManager.EditShow(show, false); return Ok(); } @@ -79,7 +82,6 @@ namespace Kyoo.Api Show show = _database.Shows.Include(x => x.ExternalIDs).FirstOrDefault(x => x.Slug == slug); if (show == null) return NotFound(); - show.ExternalIDs = _libraryManager.Validate(externalIDs); _database.SaveChanges(); _taskManager.StartTask("re-scan", $"show/{slug}"); return Ok(); @@ -96,7 +98,7 @@ namespace Kyoo.Api [Authorize(Policy = "Write")] public async Task DownloadImages(string slug) { - Show show = _libraryManager.GetShow(slug); + Show show = await _libraryManager.GetShow(slug); if (show == null) return NotFound(); await _thumbnailsManager.Validate(show, true); diff --git a/Kyoo/Views/API/StudioAPI.cs b/Kyoo/Views/API/StudioAPI.cs index 4437d5ca..59228155 100644 --- a/Kyoo/Views/API/StudioAPI.cs +++ b/Kyoo/Views/API/StudioAPI.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Kyoo.Controllers; using Kyoo.Models; using Microsoft.AspNetCore.Mvc; @@ -18,9 +19,9 @@ namespace Kyoo.API _libraryManager = libraryManager; } - public ActionResult> Index() + public async Task>> Index() { - return _libraryManager.GetStudios().ToList(); + return (await _libraryManager.GetStudios()).ToList(); } } } \ No newline at end of file diff --git a/Kyoo/Views/API/SubtitleAPI.cs b/Kyoo/Views/API/SubtitleAPI.cs index df875fca..37685101 100644 --- a/Kyoo/Views/API/SubtitleAPI.cs +++ b/Kyoo/Views/API/SubtitleAPI.cs @@ -14,31 +14,37 @@ namespace Kyoo.Api public class SubtitleController : ControllerBase { private readonly ILibraryManager _libraryManager; - private readonly ITranscoder _transcoder; + //private readonly ITranscoder _transcoder; - public SubtitleController(ILibraryManager libraryManager, ITranscoder transcoder) + public SubtitleController(ILibraryManager libraryManager/*, ITranscoder transcoder*/) { _libraryManager = libraryManager; - _transcoder = transcoder; + // _transcoder = transcoder; } [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}.{identifier}.{extension?}")] [Authorize(Policy="Play")] - public IActionResult GetSubtitle(string showSlug, int seasonNumber, int episodeNumber, string identifier, string extension) + public async Task GetSubtitle(string showSlug, + int seasonNumber, + int episodeNumber, + string identifier, + string extension) { string languageTag = identifier.Length == 3 ? identifier.Substring(0, 3) : null; bool forced = identifier.Length > 4 && identifier.Substring(4) == "forced"; Track subtitle = null; if (languageTag != null) - subtitle = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Tracks + subtitle = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Tracks .FirstOrDefault(x => x.Language == languageTag && x.IsForced == forced); if (subtitle == null) { - string idString = identifier.IndexOf('-') != -1 ? identifier.Substring(0, identifier.IndexOf('-')) : identifier; - long.TryParse(idString, out long id); - subtitle = _libraryManager.GetTracks().FirstOrDefault(x => x.ID == id); + string idString = identifier.IndexOf('-') != -1 + ? identifier.Substring(0, identifier.IndexOf('-')) + : identifier; + int.TryParse(idString, out int id); + subtitle = await _libraryManager.GetTrack(id); } if (subtitle == null) @@ -57,43 +63,43 @@ namespace Kyoo.Api return PhysicalFile(subtitle.Path, mime); } - [HttpGet("extract/{showSlug}-s{seasonNumber}e{episodeNumber}")] - [Authorize(Policy="Admin")] - public async Task ExtractSubtitle(string showSlug, long seasonNumber, long episodeNumber) - { - Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); - episode.Tracks = null; - - Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path); - foreach (Track track in tracks) - { - track.EpisodeID = episode.ID; - _libraryManager.Register(track); - } - await _libraryManager.SaveChanges(); - return "Done. " + tracks.Length + " track(s) extracted."; - } - - [HttpGet("extract/{showSlug}")] - [Authorize(Policy="Admin")] - public async Task ExtractSubtitle(string showSlug) - { - IEnumerable episodes = _libraryManager.GetShow(showSlug).Episodes; - foreach (Episode episode in episodes) - { - episode.Tracks = null; - - Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path); - foreach (Track track in tracks) - { - track.EpisodeID = episode.ID; - _libraryManager.Register(track); - } - await _libraryManager.SaveChanges(); - } - - return "Done."; - } + // [HttpGet("extract/{showSlug}-s{seasonNumber}e{episodeNumber}")] + // [Authorize(Policy="Admin")] + // public async Task ExtractSubtitle(string showSlug, long seasonNumber, long episodeNumber) + // { + // Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); + // episode.Tracks = null; + // + // Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path); + // foreach (Track track in tracks) + // { + // track.EpisodeID = episode.ID; + // _libraryManager.Register(track); + // } + // await _libraryManager.SaveChanges(); + // return "Done. " + tracks.Length + " track(s) extracted."; + // } + // + // [HttpGet("extract/{showSlug}")] + // [Authorize(Policy="Admin")] + // public async Task ExtractSubtitle(string showSlug) + // { + // IEnumerable episodes = _libraryManager.GetShow(showSlug).Episodes; + // foreach (Episode episode in episodes) + // { + // episode.Tracks = null; + // + // Track[] tracks = await _transcoder.ExtractSubtitles(episode.Path); + // foreach (Track track in tracks) + // { + // track.EpisodeID = episode.ID; + // _libraryManager.Register(track); + // } + // await _libraryManager.SaveChanges(); + // } + // + // return "Done."; + // } } diff --git a/Kyoo/Views/API/ThumbnailAPI.cs b/Kyoo/Views/API/ThumbnailAPI.cs index 22b376e8..455c680d 100644 --- a/Kyoo/Views/API/ThumbnailAPI.cs +++ b/Kyoo/Views/API/ThumbnailAPI.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using System.IO; +using System.Threading.Tasks; using Kyoo.Controllers; using Microsoft.AspNetCore.Authorization; @@ -20,9 +21,9 @@ namespace Kyoo.Api [HttpGet("poster/{showSlug}")] [Authorize(Policy="Read")] - public IActionResult GetShowThumb(string showSlug) + public async Task GetShowThumb(string showSlug) { - string path = _libraryManager.GetShow(showSlug)?.Path; + string path = (await _libraryManager.GetShow(showSlug))?.Path; if (path == null) return NotFound(); @@ -35,9 +36,9 @@ namespace Kyoo.Api [HttpGet("logo/{showSlug}")] [Authorize(Policy="Read")] - public IActionResult GetShowLogo(string showSlug) + public async Task GetShowLogo(string showSlug) { - string path = _libraryManager.GetShow(showSlug)?.Path; + string path = (await _libraryManager.GetShow(showSlug))?.Path; if (path == null) return NotFound(); @@ -50,9 +51,9 @@ namespace Kyoo.Api [HttpGet("backdrop/{showSlug}")] [Authorize(Policy="Read")] - public IActionResult GetShowBackdrop(string showSlug) + public async Task GetShowBackdrop(string showSlug) { - string path = _libraryManager.GetShow(showSlug)?.Path; + string path = (await _libraryManager.GetShow(showSlug))?.Path; if (path == null) return NotFound(); @@ -76,9 +77,9 @@ namespace Kyoo.Api [HttpGet("thumb/{showSlug}-s{seasonNumber}e{episodeNumber}")] [Authorize(Policy="Read")] - public IActionResult GetEpisodeThumb(string showSlug, long seasonNumber, long episodeNumber) + public async Task GetEpisodeThumb(string showSlug, int seasonNumber, int episodeNumber) { - string path = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Path; + string path = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Path; if (path == null) return NotFound(); diff --git a/Kyoo/Views/API/VideoAPI.cs b/Kyoo/Views/API/VideoAPI.cs index 57a77833..2c12cea7 100644 --- a/Kyoo/Views/API/VideoAPI.cs +++ b/Kyoo/Views/API/VideoAPI.cs @@ -27,9 +27,9 @@ namespace Kyoo.Api [HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")] [Authorize(Policy="Play")] - public IActionResult Index(string showSlug, long seasonNumber, long episodeNumber) + public async Task Index(string showSlug, int seasonNumber, int episodeNumber) { - Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); + Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); if (episode != null && System.IO.File.Exists(episode.Path)) return PhysicalFile(episode.Path, "video/x-matroska", true); @@ -38,9 +38,9 @@ namespace Kyoo.Api [HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}")] [Authorize(Policy="Play")] - public async Task Transmux(string showSlug, long seasonNumber, long episodeNumber) + public async Task Transmux(string showSlug, int seasonNumber, int episodeNumber) { - Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); + Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); if (episode == null || !System.IO.File.Exists(episode.Path)) return NotFound(); @@ -61,9 +61,9 @@ namespace Kyoo.Api [HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")] [Authorize(Policy="Play")] - public async Task Transcode(string showSlug, long seasonNumber, long episodeNumber) + public async Task Transcode(string showSlug, int seasonNumber, int episodeNumber) { - Episode episode = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); + Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); if (episode == null || !System.IO.File.Exists(episode.Path)) return NotFound(); @@ -85,9 +85,9 @@ namespace Kyoo.Api [HttpGet("{movieSlug}")] [Authorize(Policy="Play")] - public IActionResult Index(string movieSlug) + public async Task Index(string movieSlug) { - Episode episode = _libraryManager.GetMovieEpisode(movieSlug); + Episode episode = await _libraryManager.GetMovieEpisode(movieSlug); if (episode != null && System.IO.File.Exists(episode.Path)) return PhysicalFile(episode.Path, "video/webm", true); @@ -98,7 +98,7 @@ namespace Kyoo.Api [Authorize(Policy="Play")] public async Task Transmux(string movieSlug) { - Episode episode = _libraryManager.GetMovieEpisode(movieSlug); + Episode episode = await _libraryManager.GetMovieEpisode(movieSlug); if (episode == null || !System.IO.File.Exists(episode.Path)) return NotFound(); @@ -112,7 +112,7 @@ namespace Kyoo.Api [Authorize(Policy="Play")] public async Task Transcode(string movieSlug) { - Episode episode = _libraryManager.GetMovieEpisode(movieSlug); + Episode episode = await _libraryManager.GetMovieEpisode(movieSlug); if (episode == null || !System.IO.File.Exists(episode.Path)) return NotFound(); diff --git a/Kyoo/Views/API/WatchAPI.cs b/Kyoo/Views/API/WatchAPI.cs index d8cc84c2..07039073 100644 --- a/Kyoo/Views/API/WatchAPI.cs +++ b/Kyoo/Views/API/WatchAPI.cs @@ -1,4 +1,5 @@ -using Kyoo.Controllers; +using System.Threading.Tasks; +using Kyoo.Controllers; using Kyoo.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -18,9 +19,9 @@ namespace Kyoo.Api [HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")] [Authorize(Policy="Read")] - public ActionResult Index(string showSlug, long seasonNumber, long episodeNumber) + public async Task> Index(string showSlug, int seasonNumber, int episodeNumber) { - Episode item = _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); + Episode item = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); if(item == null) return NotFound(); @@ -30,9 +31,9 @@ namespace Kyoo.Api [HttpGet("{movieSlug}")] [Authorize(Policy="Read")] - public ActionResult Index(string movieSlug) + public async Task> Index(string movieSlug) { - Episode item = _libraryManager.GetMovieEpisode(movieSlug); + Episode item = await _libraryManager.GetMovieEpisode(movieSlug); if(item == null) return NotFound();