diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index e1587908..caef30a6 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; using Kyoo.Models; @@ -15,7 +16,17 @@ namespace Kyoo.Controllers Task Create([NotNull] T obj); Task CreateIfNotExists([NotNull] T obj); Task Edit([NotNull] T edited, bool resetOld); - Task Delete(T obj); + + Task Delete(int id); + Task Delete(string slug); + Task Delete([NotNull] T obj); + + Task DeleteRange(params T[] objs) => DeleteRange(objs.AsEnumerable()); + Task DeleteRange(IEnumerable objs); + Task DeleteRange(params int[] ids) => DeleteRange(ids.AsEnumerable()); + Task DeleteRange(IEnumerable ids); + Task DeleteRange(params string[] slugs) => DeleteRange(slugs.AsEnumerable()); + Task DeleteRange(IEnumerable slugs); } public interface IShowRepository : IRepository @@ -27,6 +38,7 @@ namespace Kyoo.Controllers public interface ISeasonRepository : IRepository { Task Get(string showSlug, int seasonNumber); + Task Delete(string showSlug, int seasonNumber); Task> GetSeasons(int showID); Task> GetSeasons(string showSlug); @@ -35,6 +47,7 @@ namespace Kyoo.Controllers public interface IEpisodeRepository : IRepository { Task Get(string showSlug, int seasonNumber, int episodeNumber); + Task Delete(string showSlug, int seasonNumber, int episodeNumber); Task> GetEpisodes(int showID, int seasonNumber); Task> GetEpisodes(string showSlug, int seasonNumber); diff --git a/Kyoo.Common/Models/Collection.cs b/Kyoo.Common/Models/Collection.cs index 98881da2..5336c52c 100644 --- a/Kyoo.Common/Models/Collection.cs +++ b/Kyoo.Common/Models/Collection.cs @@ -19,6 +19,14 @@ namespace Kyoo.Models get => Links.Select(x => x.Show); set => Links = value.Select(x => new CollectionLink(this, x)); } + + [NotMergable] [JsonIgnore] public virtual IEnumerable LibraryLinks { get; set; } + + [NotMergable] [JsonIgnore] public IEnumerable Libraries + { + get => LibraryLinks?.Select(x => x.Library); + set => LibraryLinks = value?.Select(x => new LibraryLink(x, this)); + } public Collection() { } diff --git a/Kyoo.Common/Models/Genre.cs b/Kyoo.Common/Models/Genre.cs index 1935189e..1f964eff 100644 --- a/Kyoo.Common/Models/Genre.cs +++ b/Kyoo.Common/Models/Genre.cs @@ -1,4 +1,7 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Linq; +using Kyoo.Models.Attributes; +using Newtonsoft.Json; namespace Kyoo.Models { @@ -8,7 +11,13 @@ namespace Kyoo.Models public string Slug { get; set; } public string Name { get; set; } - // public IEnumerable Shows { get; set; } + [NotMergable] [JsonIgnore] public virtual IEnumerable Links { get; set; } + + [NotMergable] [JsonIgnore] public IEnumerable Shows + { + get => Links.Select(x => x.Show); + set => Links = value?.Select(x => new GenreLink(x, this)); + } public Genre() {} diff --git a/Kyoo.Common/Models/Show.cs b/Kyoo.Common/Models/Show.cs index a79ef71e..959856ee 100644 --- a/Kyoo.Common/Models/Show.cs +++ b/Kyoo.Common/Models/Show.cs @@ -41,6 +41,22 @@ namespace Kyoo.Models [JsonIgnore] public virtual IEnumerable People { get; set; } [JsonIgnore] public virtual IEnumerable Seasons { get; set; } [JsonIgnore] public virtual IEnumerable Episodes { get; set; } + + [NotMergable] [JsonIgnore] public virtual IEnumerable LibraryLinks { get; set; } + + [NotMergable] [JsonIgnore] public IEnumerable Libraries + { + get => LibraryLinks?.Select(x => x.Library); + set => LibraryLinks = value?.Select(x => new LibraryLink(x, this)); + } + + [NotMergable] [JsonIgnore] public virtual IEnumerable CollectionLinks { get; set; } + + [NotMergable] [JsonIgnore] public IEnumerable Collections + { + get => CollectionLinks.Select(x => x.Collection); + set => CollectionLinks = value?.Select(x => new CollectionLink(x, this)); + } public Show() { } diff --git a/Kyoo/Controllers/Repositories/CollectionRepository.cs b/Kyoo/Controllers/Repositories/CollectionRepository.cs index 6e0151ef..776cc597 100644 --- a/Kyoo/Controllers/Repositories/CollectionRepository.cs +++ b/Kyoo/Controllers/Repositories/CollectionRepository.cs @@ -111,10 +111,49 @@ namespace Kyoo.Controllers await _database.SaveChangesAsync(); } + public async Task Delete(int id) + { + Collection obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Collection obj = await Get(slug); + await Delete(obj); + } + public async Task Delete(Collection obj) { - _database.Collections.Remove(obj); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + if (obj.Links != null) + foreach (CollectionLink link in obj.Links) + _database.Entry(link).State = EntityState.Deleted; + if (obj.LibraryLinks != null) + foreach (LibraryLink link in obj.LibraryLinks) + _database.Entry(link).State = EntityState.Deleted; await _database.SaveChangesAsync(); } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Collection obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs index 4c698bbc..e4d54134 100644 --- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -12,6 +12,7 @@ namespace Kyoo.Controllers { private readonly DatabaseContext _database; private readonly IProviderRepository _providers; + // private readonly ITrackRepository _tracks; public EpisodeRepository(DatabaseContext database, IProviderRepository providers) @@ -83,10 +84,12 @@ namespace Kyoo.Controllers if (obj.ExternalIDs != null) foreach (MetadataID entry in obj.ExternalIDs) _database.Entry(entry).State = EntityState.Added; + + // Since Episodes & Tracks are on the same DB, using a single commit is quicker. if (obj.Tracks != null) foreach (Track entry in obj.Tracks) _database.Entry(entry).State = EntityState.Added; - + try { await _database.SaveChangesAsync(); @@ -99,6 +102,16 @@ namespace Kyoo.Controllers throw new DuplicatedItemException($"Trying to insert a duplicated episode (slug {obj.Slug} already exists)."); throw; } + + // Since Episodes & Tracks are on the same DB, using a single commit is quicker. + /*if (obj.Tracks != null) + * foreach (Track track in obj.Tracks) + * { + * track.EpisodeID = obj.ID; + * await _tracks.Create(track); + * } + */ + return obj.ID; } @@ -153,16 +166,10 @@ namespace Kyoo.Controllers } } - 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(); + && x.SeasonNumber == seasonNumber).ToListAsync(); } public async Task> GetEpisodes(string showSlug, int seasonNumber) @@ -175,5 +182,54 @@ namespace Kyoo.Controllers { return await _database.Episodes.Where(x => x.SeasonID == seasonID).ToListAsync(); } + + public async Task Delete(int id) + { + Episode obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Episode obj = await Get(slug); + await Delete(obj); + } + + public async Task Delete(string showSlug, int seasonNumber, int episodeNumber) + { + Episode obj = await Get(showSlug, seasonNumber, episodeNumber); + await Delete(obj); + } + + public async Task Delete(Episode obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Deleted; + // Since Tracks & Episodes are on the same database and handled by dotnet-ef, we can't use the repository to delete them. + await _database.SaveChangesAsync(); + } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Episode obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/GenreRepository.cs b/Kyoo/Controllers/Repositories/GenreRepository.cs index a49249a8..9834e37e 100644 --- a/Kyoo/Controllers/Repositories/GenreRepository.cs +++ b/Kyoo/Controllers/Repositories/GenreRepository.cs @@ -56,7 +56,7 @@ namespace Kyoo.Controllers if (obj == null) throw new ArgumentNullException(nameof(obj)); - await _database.Genres.AddAsync(obj); + _database.Entry(obj).State = EntityState.Added; try { @@ -110,11 +110,47 @@ namespace Kyoo.Controllers Utility.Merge(old, edited); await _database.SaveChangesAsync(); } + + public async Task Delete(int id) + { + Genre obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Genre obj = await Get(slug); + await Delete(obj); + } public async Task Delete(Genre obj) { - _database.Genres.Remove(obj); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + if (obj.Links != null) + foreach (GenreLink link in obj.Links) + _database.Entry(link).State = EntityState.Deleted; await _database.SaveChangesAsync(); } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Genre obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/LibraryRepository.cs b/Kyoo/Controllers/Repositories/LibraryRepository.cs index 133b47fe..99e15a0f 100644 --- a/Kyoo/Controllers/Repositories/LibraryRepository.cs +++ b/Kyoo/Controllers/Repositories/LibraryRepository.cs @@ -124,10 +124,49 @@ namespace Kyoo.Controllers link.ProviderID = await _providers.CreateIfNotExists(link.Provider); } + public async Task Delete(int id) + { + Library obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Library obj = await Get(slug); + await Delete(obj); + } + public async Task Delete(Library obj) { - _database.Libraries.Remove(obj); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + if (obj.ProviderLinks != null) + foreach (ProviderLink entry in obj.ProviderLinks) + _database.Entry(entry).State = EntityState.Deleted; + if (obj.Links != null) + foreach (LibraryLink entry in obj.Links) + _database.Entry(entry).State = EntityState.Deleted; await _database.SaveChangesAsync(); } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Library obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/PeopleRepository.cs b/Kyoo/Controllers/Repositories/PeopleRepository.cs index a1689f25..85664970 100644 --- a/Kyoo/Controllers/Repositories/PeopleRepository.cs +++ b/Kyoo/Controllers/Repositories/PeopleRepository.cs @@ -122,11 +122,50 @@ namespace Kyoo.Controllers foreach (MetadataID link in obj.ExternalIDs) link.ProviderID = await _providers.CreateIfNotExists(link.Provider); } + + public async Task Delete(int id) + { + People obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + People obj = await Get(slug); + await Delete(obj); + } public async Task Delete(People obj) { - _database.Peoples.Remove(obj); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Deleted; + if (obj.Roles != null) + foreach (PeopleLink link in obj.Roles) + _database.Entry(link).State = EntityState.Deleted; await _database.SaveChangesAsync(); } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (People obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/ProviderRepository.cs b/Kyoo/Controllers/Repositories/ProviderRepository.cs index ab1abee9..ec298411 100644 --- a/Kyoo/Controllers/Repositories/ProviderRepository.cs +++ b/Kyoo/Controllers/Repositories/ProviderRepository.cs @@ -110,10 +110,44 @@ namespace Kyoo.Controllers await _database.SaveChangesAsync(); } + public async Task Delete(int id) + { + ProviderID obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + ProviderID obj = await Get(slug); + await Delete(obj); + } + public async Task Delete(ProviderID obj) { - _database.Providers.Remove(obj); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + // TODO handle ExternalID deletion when they refer to this providerID. await _database.SaveChangesAsync(); } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (ProviderID obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs index da117095..c9433c72 100644 --- a/Kyoo/Controllers/Repositories/SeasonRepository.cs +++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs @@ -12,12 +12,14 @@ namespace Kyoo.Controllers { private readonly DatabaseContext _database; private readonly IProviderRepository _providers; + private readonly IEpisodeRepository _episodes; - public SeasonRepository(DatabaseContext database, IProviderRepository providers) + public SeasonRepository(DatabaseContext database, IProviderRepository providers, IEpisodeRepository episodes) { _database = database; _providers = providers; + _episodes = episodes; } public void Dispose() @@ -141,12 +143,6 @@ namespace Kyoo.Controllers link.ProviderID = await _providers.CreateIfNotExists(link.Provider); } } - - public async Task Delete(Season obj) - { - _database.Seasons.Remove(obj); - await _database.SaveChangesAsync(); - } public async Task> GetSeasons(int showID) { @@ -157,5 +153,58 @@ namespace Kyoo.Controllers { return await _database.Seasons.Where(x => x.Show.Slug == showSlug).ToListAsync(); } + + public async Task Delete(int id) + { + Season obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Season obj = await Get(slug); + await Delete(obj); + } + + public async Task Delete(string showSlug, int seasonNumber) + { + Season obj = await Get(showSlug, seasonNumber); + await Delete(obj); + } + + public async Task Delete(Season obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Deleted; + + await _database.SaveChangesAsync(); + + if (obj.Episodes != null) + await _episodes.DeleteRange(obj.Episodes); + } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Season obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs index 7783cb82..20bc7f67 100644 --- a/Kyoo/Controllers/Repositories/ShowRepository.cs +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -15,18 +15,24 @@ namespace Kyoo.Controllers private readonly IPeopleRepository _people; private readonly IGenreRepository _genres; private readonly IProviderRepository _providers; + private readonly ISeasonRepository _seasons; + private readonly IEpisodeRepository _episodes; public ShowRepository(DatabaseContext database, IStudioRepository studios, IPeopleRepository people, IGenreRepository genres, - IProviderRepository providers) + IProviderRepository providers, + ISeasonRepository seasons, + IEpisodeRepository episodes) { _database = database; _studios = studios; _people = people; _genres = genres; _providers = providers; + _seasons = seasons; + _episodes = episodes; } public void Dispose() @@ -40,19 +46,19 @@ namespace Kyoo.Controllers await Task.WhenAll(_database.DisposeAsync().AsTask(), _studios.DisposeAsync().AsTask()); } - public async Task Get(int id) + public Task Get(int id) { - return await _database.Shows.FirstOrDefaultAsync(x => x.ID == id); + return _database.Shows.FirstOrDefaultAsync(x => x.ID == id); } - public async Task Get(string slug) + public Task Get(string slug) { - return await _database.Shows.FirstOrDefaultAsync(x => x.Slug == slug); + return _database.Shows.FirstOrDefaultAsync(x => x.Slug == slug); } - public async Task GetByPath(string path) + public Task GetByPath(string path) { - return await _database.Shows.FirstOrDefaultAsync(x => x.Path == path); + return _database.Shows.FirstOrDefaultAsync(x => x.Path == path); } public async Task> Search(string query) @@ -145,28 +151,16 @@ namespace Kyoo.Controllers obj.StudioID = await _studios.CreateIfNotExists(obj.Studio); if (obj.GenreLinks != null) - { foreach (GenreLink link in obj.GenreLinks) link.GenreID = await _genres.CreateIfNotExists(link.Genre); - } if (obj.People != null) - { foreach (PeopleLink link in obj.People) link.PeopleID = await _people.CreateIfNotExists(link.People); - } if (obj.ExternalIDs != null) - { foreach (MetadataID link in obj.ExternalIDs) link.ProviderID = await _providers.CreateIfNotExists(link.Provider); - } - } - - public async Task Delete(Show show) - { - _database.Shows.Remove(show); - await _database.SaveChangesAsync(); } public async Task AddShowLink(int showID, int? libraryID, int? collectionID) @@ -191,5 +185,71 @@ namespace Kyoo.Controllers await _database.SaveChangesAsync(); } + + public async Task Delete(int id) + { + Show obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Show obj = await Get(slug); + await Delete(obj); + } + + public async Task Delete(Show obj) + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + + if (obj.GenreLinks != null) + foreach (GenreLink entry in obj.GenreLinks) + _database.Entry(entry).State = EntityState.Deleted; + + if (obj.People != null) + foreach (PeopleLink entry in obj.People) + _database.Entry(entry).State = EntityState.Deleted; + + if (obj.ExternalIDs != null) + foreach (MetadataID entry in obj.ExternalIDs) + _database.Entry(entry).State = EntityState.Deleted; + + if (obj.CollectionLinks != null) + foreach (CollectionLink entry in obj.CollectionLinks) + _database.Entry(entry).State = EntityState.Deleted; + + if (obj.LibraryLinks != null) + foreach (LibraryLink entry in obj.LibraryLinks) + _database.Entry(entry).State = EntityState.Deleted; + + await _database.SaveChangesAsync(); + + if (obj.Seasons != null) + await _seasons.DeleteRange(obj.Seasons); + + if (obj.Episodes != null) + await _episodes.DeleteRange(obj.Episodes); + } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Show obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/StudioRepository.cs b/Kyoo/Controllers/Repositories/StudioRepository.cs index 8e7c3a02..69e93e74 100644 --- a/Kyoo/Controllers/Repositories/StudioRepository.cs +++ b/Kyoo/Controllers/Repositories/StudioRepository.cs @@ -109,10 +109,47 @@ namespace Kyoo.Controllers await _database.SaveChangesAsync(); } + public async Task Delete(int id) + { + Studio obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Studio obj = await Get(slug); + await Delete(obj); + } + public async Task Delete(Studio obj) { - _database.Studios.Remove(obj); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; + + // Using Dotnet-EF change discovery service to remove references to this studio on shows. + foreach (Show show in obj.Shows) + show.StudioID = null; await _database.SaveChangesAsync(); } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Studio obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/TrackRepository.cs b/Kyoo/Controllers/Repositories/TrackRepository.cs index 711ca9f7..dbc54b01 100644 --- a/Kyoo/Controllers/Repositories/TrackRepository.cs +++ b/Kyoo/Controllers/Repositories/TrackRepository.cs @@ -99,10 +99,43 @@ namespace Kyoo.Controllers await _database.SaveChangesAsync(); } + public async Task Delete(int id) + { + Track obj = await Get(id); + await Delete(obj); + } + + public async Task Delete(string slug) + { + Track obj = await Get(slug); + await Delete(obj); + } + public async Task Delete(Track obj) { - _database.Tracks.Remove(obj); + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + _database.Entry(obj).State = EntityState.Deleted; await _database.SaveChangesAsync(); } + + public async Task DeleteRange(IEnumerable objs) + { + foreach (Track obj in objs) + await Delete(obj); + } + + public async Task DeleteRange(IEnumerable ids) + { + foreach (int id in ids) + await Delete(id); + } + + public async Task DeleteRange(IEnumerable slugs) + { + foreach (string slug in slugs) + await Delete(slug); + } } } \ No newline at end of file diff --git a/Kyoo/Models/DatabaseContext.cs b/Kyoo/Models/DatabaseContext.cs index 86e3aea1..43c8ebfa 100644 --- a/Kyoo/Models/DatabaseContext.cs +++ b/Kyoo/Models/DatabaseContext.cs @@ -113,16 +113,40 @@ namespace Kyoo .Ignore(x => x.Providers); modelBuilder.Entity() - .Ignore(x => x.Shows); + .Ignore(x => x.Shows) + .Ignore(x => x.Libraries); modelBuilder.Entity() - .Ignore(x => x.Genres); - + .Ignore(x => x.Genres) + .Ignore(x => x.Libraries) + .Ignore(x => x.Collections); + modelBuilder.Entity() .Ignore(x => x.Slug) .Ignore(x => x.Name) .Ignore(x => x.ExternalIDs); + modelBuilder.Entity() + .Ignore(x => x.Shows); + + + modelBuilder.Entity() + .HasOne(x => x.Show) + .WithMany(x => x.ExternalIDs) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasOne(x => x.Season) + .WithMany(x => x.ExternalIDs) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasOne(x => x.Episode) + .WithMany(x => x.ExternalIDs) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasOne(x => x.People) + .WithMany(x => x.ExternalIDs) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() .HasIndex(x => x.Slug) diff --git a/Kyoo/Models/DatabaseMigrations/Internal/20200618133537_Initial.Designer.cs b/Kyoo/Models/DatabaseMigrations/Internal/20200623000428_Initial.Designer.cs similarity index 97% rename from Kyoo/Models/DatabaseMigrations/Internal/20200618133537_Initial.Designer.cs rename to Kyoo/Models/DatabaseMigrations/Internal/20200623000428_Initial.Designer.cs index 5b5a5668..1467c44e 100644 --- a/Kyoo/Models/DatabaseMigrations/Internal/20200618133537_Initial.Designer.cs +++ b/Kyoo/Models/DatabaseMigrations/Internal/20200623000428_Initial.Designer.cs @@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Kyoo.Models.DatabaseMigrations.Internal { [DbContext(typeof(DatabaseContext))] - [Migration("20200618133537_Initial")] + [Migration("20200623000428_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -506,7 +506,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal .HasForeignKey("CollectionID"); b.HasOne("Kyoo.Models.Show", "Show") - .WithMany() + .WithMany("CollectionLinks") .HasForeignKey("ShowID") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -528,7 +528,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.GenreLink", b => { b.HasOne("Kyoo.Models.Genre", "Genre") - .WithMany() + .WithMany("Links") .HasForeignKey("GenreID") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -543,7 +543,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.LibraryLink", b => { b.HasOne("Kyoo.Models.Collection", "Collection") - .WithMany() + .WithMany("LibraryLinks") .HasForeignKey("CollectionID"); b.HasOne("Kyoo.Models.Library", "Library") @@ -553,7 +553,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal .IsRequired(); b.HasOne("Kyoo.Models.Show", "Show") - .WithMany() + .WithMany("LibraryLinks") .HasForeignKey("ShowID"); }); @@ -561,11 +561,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal { b.HasOne("Kyoo.Models.Episode", "Episode") .WithMany("ExternalIDs") - .HasForeignKey("EpisodeID"); + .HasForeignKey("EpisodeID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.People", "People") .WithMany("ExternalIDs") - .HasForeignKey("PeopleID"); + .HasForeignKey("PeopleID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.ProviderID", "Provider") .WithMany() @@ -575,11 +577,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.HasOne("Kyoo.Models.Season", "Season") .WithMany("ExternalIDs") - .HasForeignKey("SeasonID"); + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.Show", "Show") .WithMany("ExternalIDs") - .HasForeignKey("ShowID"); + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Kyoo.Models.PeopleLink", b => diff --git a/Kyoo/Models/DatabaseMigrations/Internal/20200618133537_Initial.cs b/Kyoo/Models/DatabaseMigrations/Internal/20200623000428_Initial.cs similarity index 99% rename from Kyoo/Models/DatabaseMigrations/Internal/20200618133537_Initial.cs rename to Kyoo/Models/DatabaseMigrations/Internal/20200623000428_Initial.cs index 6890dbb9..9151e80b 100644 --- a/Kyoo/Models/DatabaseMigrations/Internal/20200618133537_Initial.cs +++ b/Kyoo/Models/DatabaseMigrations/Internal/20200623000428_Initial.cs @@ -347,13 +347,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal column: x => x.EpisodeID, principalTable: "Episodes", principalColumn: "ID", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_MetadataIds_Peoples_PeopleID", column: x => x.PeopleID, principalTable: "Peoples", principalColumn: "ID", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_MetadataIds_Providers_ProviderID", column: x => x.ProviderID, @@ -365,13 +365,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal column: x => x.SeasonID, principalTable: "Seasons", principalColumn: "ID", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_MetadataIds_Shows_ShowID", column: x => x.ShowID, principalTable: "Shows", principalColumn: "ID", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( diff --git a/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs b/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs index 4b4f404d..d71bb43d 100644 --- a/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs +++ b/Kyoo/Models/DatabaseMigrations/Internal/DatabaseContextModelSnapshot.cs @@ -504,7 +504,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal .HasForeignKey("CollectionID"); b.HasOne("Kyoo.Models.Show", "Show") - .WithMany() + .WithMany("CollectionLinks") .HasForeignKey("ShowID") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -526,7 +526,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.GenreLink", b => { b.HasOne("Kyoo.Models.Genre", "Genre") - .WithMany() + .WithMany("Links") .HasForeignKey("GenreID") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -541,7 +541,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal modelBuilder.Entity("Kyoo.Models.LibraryLink", b => { b.HasOne("Kyoo.Models.Collection", "Collection") - .WithMany() + .WithMany("LibraryLinks") .HasForeignKey("CollectionID"); b.HasOne("Kyoo.Models.Library", "Library") @@ -551,7 +551,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal .IsRequired(); b.HasOne("Kyoo.Models.Show", "Show") - .WithMany() + .WithMany("LibraryLinks") .HasForeignKey("ShowID"); }); @@ -559,11 +559,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal { b.HasOne("Kyoo.Models.Episode", "Episode") .WithMany("ExternalIDs") - .HasForeignKey("EpisodeID"); + .HasForeignKey("EpisodeID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.People", "People") .WithMany("ExternalIDs") - .HasForeignKey("PeopleID"); + .HasForeignKey("PeopleID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.ProviderID", "Provider") .WithMany() @@ -573,11 +575,13 @@ namespace Kyoo.Models.DatabaseMigrations.Internal b.HasOne("Kyoo.Models.Season", "Season") .WithMany("ExternalIDs") - .HasForeignKey("SeasonID"); + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Kyoo.Models.Show", "Show") .WithMany("ExternalIDs") - .HasForeignKey("ShowID"); + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Kyoo.Models.PeopleLink", b => diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index 65f99812..3e45db4b 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -46,8 +46,8 @@ namespace Kyoo services.AddDbContext(options => { options.UseLazyLoadingProxies() - .UseNpgsql(_configuration.GetConnectionString("Database")) - .EnableSensitiveDataLogging(); + .UseNpgsql(_configuration.GetConnectionString("Database")); + // .EnableSensitiveDataLogging() // .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole())); }, ServiceLifetime.Transient); diff --git a/Kyoo/Tasks/Crawler.cs b/Kyoo/Tasks/Crawler.cs index e89347f0..91747974 100644 --- a/Kyoo/Tasks/Crawler.cs +++ b/Kyoo/Tasks/Crawler.cs @@ -58,6 +58,11 @@ namespace Kyoo.Controllers using IServiceScope serviceScope = _serviceProvider.CreateScope(); await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService(); + + foreach (Show show in await libraryManager.GetShows()) + if (!Directory.Exists(show.Path)) + await libraryManager.DeleteShow(show); + ICollection episodes = await libraryManager.GetEpisodes(); ICollection libraries = argument == null ? await libraryManager.GetLibraries() @@ -135,12 +140,12 @@ namespace Kyoo.Controllers { if (token.IsCancellationRequested) return; - - try + + try { using IServiceScope serviceScope = _serviceProvider.CreateScope(); await using ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService(); - + string patern = _config.GetValue("regex"); Regex regex = new Regex(patern, RegexOptions.IgnoreCase); Match match = regex.Match(relativePath); @@ -160,12 +165,12 @@ namespace Kyoo.Controllers else { Season season = await GetSeason(libraryManager, show, seasonNumber, library); - Episode episode = await GetEpisode(libraryManager, - show, - season, - episodeNumber, + Episode episode = await GetEpisode(libraryManager, + show, + season, + episodeNumber, absoluteNumber, - path, + path, library); await libraryManager.RegisterEpisode(episode); } @@ -173,6 +178,10 @@ namespace Kyoo.Controllers await libraryManager.AddShowLink(show, library, collection); Console.WriteLine($"Episode at {path} registered."); } + catch (DuplicatedItemException ex) + { + await Console.Error.WriteLineAsync($"{path}: {ex.Message}"); + } catch (Exception ex) { await Console.Error.WriteLineAsync($"Unknown exception thrown while registering episode at {path}." + diff --git a/Kyoo/regexTesterr.sh b/Kyoo/regexTester.sh similarity index 100% rename from Kyoo/regexTesterr.sh rename to Kyoo/regexTester.sh