diff --git a/Kyoo.Common/Models/LibraryItem.cs b/Kyoo.Common/Models/LibraryItem.cs
index dda95343..88fd799a 100644
--- a/Kyoo.Common/Models/LibraryItem.cs
+++ b/Kyoo.Common/Models/LibraryItem.cs
@@ -63,7 +63,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/{Type:l}/{Slug}/poster")]
- public string Poster => Images[Thumbnails.Poster];
+ public string Poster => Images?.GetValueOrDefault(Thumbnails.Poster);
///
/// The type of this item (ether a collection, a show or a movie).
diff --git a/Kyoo.Common/Models/Resources/Collection.cs b/Kyoo.Common/Models/Resources/Collection.cs
index a8b796f2..ad85ad34 100644
--- a/Kyoo.Common/Models/Resources/Collection.cs
+++ b/Kyoo.Common/Models/Resources/Collection.cs
@@ -30,7 +30,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/collection/{Slug}/poster")]
- public string Poster => Images[Thumbnails.Poster];
+ public string Poster => Images?.GetValueOrDefault(Thumbnails.Poster);
///
/// The description of this collection.
diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs
index 990ce5b6..2ab30437 100644
--- a/Kyoo.Common/Models/Resources/Episode.cs
+++ b/Kyoo.Common/Models/Resources/Episode.cs
@@ -108,7 +108,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/episodes/{Slug}/thumb")]
- public string Thumb => Images[Thumbnails.Thumbnail];
+ public string Thumb => Images?.GetValueOrDefault(Thumbnails.Thumbnail);
///
/// The title of this episode.
diff --git a/Kyoo.Common/Models/Resources/People.cs b/Kyoo.Common/Models/Resources/People.cs
index 5b7f6864..d555a10d 100644
--- a/Kyoo.Common/Models/Resources/People.cs
+++ b/Kyoo.Common/Models/Resources/People.cs
@@ -28,7 +28,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/people/{Slug}/poster")]
- public string Poster => Images[Thumbnails.Poster];
+ public string Poster => Images?.GetValueOrDefault(Thumbnails.Poster);
///
[EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; }
diff --git a/Kyoo.Common/Models/Resources/Provider.cs b/Kyoo.Common/Models/Resources/Provider.cs
index 630a2880..920b7345 100644
--- a/Kyoo.Common/Models/Resources/Provider.cs
+++ b/Kyoo.Common/Models/Resources/Provider.cs
@@ -31,7 +31,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/providers/{Slug}/logo")]
- public string Logo => Images[Thumbnails.Logo];
+ public string Logo => Images?.GetValueOrDefault(Thumbnails.Logo);
///
/// The extension of the logo. This is used for http responses.
diff --git a/Kyoo.Common/Models/Resources/Season.cs b/Kyoo.Common/Models/Resources/Season.cs
index fddb7eb4..46639c3c 100644
--- a/Kyoo.Common/Models/Resources/Season.cs
+++ b/Kyoo.Common/Models/Resources/Season.cs
@@ -83,7 +83,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/seasons/{Slug}/thumb")]
- public string Poster => Images[Thumbnails.Poster];
+ public string Poster => Images?.GetValueOrDefault(Thumbnails.Poster);
///
[EditableRelation] [LoadableRelation] public ICollection ExternalIDs { get; set; }
diff --git a/Kyoo.Common/Models/Resources/Show.cs b/Kyoo.Common/Models/Resources/Show.cs
index 90aa98f8..afaaa481 100644
--- a/Kyoo.Common/Models/Resources/Show.cs
+++ b/Kyoo.Common/Models/Resources/Show.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using JetBrains.Annotations;
using Kyoo.Common.Models.Attributes;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
@@ -49,7 +47,7 @@ namespace Kyoo.Models
/// An URL to a trailer. This could be any path supported by the .
///
/// TODO for now, this is set to a youtube url. It should be cached and converted to a local file.
- public string TrailerUrl => Images[Thumbnails.Trailer];
+ public string TrailerUrl => Images?.GetValueOrDefault(Thumbnails.Trailer);
///
/// The date this show started airing. It can be null if this is unknown.
@@ -72,7 +70,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/shows/{Slug}/poster")]
- public string Poster => Images[Thumbnails.Poster];
+ public string Poster => Images?.GetValueOrDefault(Thumbnails.Poster);
///
/// The path of this show's logo.
@@ -80,7 +78,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/shows/{Slug}/logo")]
- public string Logo => Images[Thumbnails.Logo];
+ public string Logo => Images?.GetValueOrDefault(Thumbnails.Logo);
///
/// The path of this show's backdrop.
@@ -88,7 +86,7 @@ namespace Kyoo.Models
/// This can be disabled using the internal query flag.
///
[SerializeAs("{HOST}/api/shows/{Slug}/backdrop")]
- public string Backdrop => Images[Thumbnails.Thumbnail];
+ public string Backdrop => Images?.GetValueOrDefault(Thumbnails.Thumbnail);
///
/// True if this show represent a movie, false otherwise.
diff --git a/Kyoo.SqLite/Migrations/20210728135127_Triggers.cs b/Kyoo.SqLite/Migrations/20210728135127_Triggers.cs
index f26ab7e2..fe46cd26 100644
--- a/Kyoo.SqLite/Migrations/20210728135127_Triggers.cs
+++ b/Kyoo.SqLite/Migrations/20210728135127_Triggers.cs
@@ -165,7 +165,7 @@ namespace Kyoo.SqLite.Migrations
INNER JOIN Collections AS c ON l.FirstID = c.ID
WHERE s.ID = l.SecondID))
UNION ALL
- SELECT -c0.ID, c0.Slug, c0.Name AS Title, c0.Overview, 3 AS Status,
+ SELECT -c0.ID, c0.Slug, c0.Name AS Title, c0.Overview, 0 AS Status,
NULL AS StartAir, NULL AS EndAir, c0.Images, 2 AS Type
FROM collections AS c0");
}
diff --git a/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs b/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs
index c5639dbb..e6a913d2 100644
--- a/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs
+++ b/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs
@@ -55,7 +55,7 @@ namespace Kyoo.Tests.Database
[Fact]
public async Task GetCollectionTests()
{
- LibraryItem expected = new(TestSample.Get());
+ LibraryItem expected = new(TestSample.Get());
LibraryItem actual = await _repository.Get(-1);
KAssert.DeepEqual(expected, actual);
}
diff --git a/Kyoo.Tests/Database/SpecificTests/ShowTests.cs b/Kyoo.Tests/Database/SpecificTests/ShowTests.cs
index 42ab693c..c7b89be6 100644
--- a/Kyoo.Tests/Database/SpecificTests/ShowTests.cs
+++ b/Kyoo.Tests/Database/SpecificTests/ShowTests.cs
@@ -235,6 +235,39 @@ namespace Kyoo.Tests.Database
expected.Studio = new Studio("studio");
Show created = await _repository.Create(expected);
KAssert.DeepEqual(expected, created);
+ await using DatabaseContext context = Repositories.Context.New();
+ Show retrieved = await context.Shows
+ .Include(x => x.ExternalIDs)
+ .Include(x => x.Genres)
+ .Include(x => x.People)
+ .Include(x => x.Studio)
+ .FirstAsync(x => x.ID == created.ID);
+ KAssert.DeepEqual(expected, retrieved);
+ }
+
+ [Fact]
+ public async Task CreateWithExternalID()
+ {
+ Show expected = TestSample.Get();
+ expected.ID = 0;
+ expected.Slug = "created-relation-test";
+ expected.ExternalIDs = new[]
+ {
+ new MetadataID
+ {
+ Provider = TestSample.Get(),
+ DataID = "ID"
+ }
+ };
+ Show created = await _repository.Create(expected);
+ KAssert.DeepEqual(expected, created);
+ await using DatabaseContext context = Repositories.Context.New();
+ Show retrieved = await context.Shows
+ .Include(x => x.ExternalIDs)
+ .FirstAsync(x => x.ID == created.ID);
+ KAssert.DeepEqual(expected, retrieved);
+ Assert.Equal(1, retrieved.ExternalIDs.Count);
+ Assert.Equal("ID", retrieved.ExternalIDs.First().DataID);
}
[Fact]
diff --git a/Kyoo.Tests/KAssert.cs b/Kyoo.Tests/KAssert.cs
index 80209c98..3acee11a 100644
--- a/Kyoo.Tests/KAssert.cs
+++ b/Kyoo.Tests/KAssert.cs
@@ -1,3 +1,5 @@
+using System.Collections;
+using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Xunit;
@@ -19,8 +21,11 @@ namespace Kyoo.Tests
[AssertionMethod]
public static void DeepEqual(T expected, T value)
{
- foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.Instance))
+ PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
+ foreach (PropertyInfo property in properties)
Assert.Equal(property.GetValue(expected), property.GetValue(value));
+ if (!properties.Any())
+ Assert.Equal(expected, value);
}
///
diff --git a/Kyoo.TheMovieDb/Convertors/ShowConvertors.cs b/Kyoo.TheMovieDb/Convertors/ShowConvertors.cs
index f379e19b..a64cfe3b 100644
--- a/Kyoo.TheMovieDb/Convertors/ShowConvertors.cs
+++ b/Kyoo.TheMovieDb/Convertors/ShowConvertors.cs
@@ -44,7 +44,6 @@ namespace Kyoo.TheMovieDb
Studio = !string.IsNullOrEmpty(tv.ProductionCompanies.FirstOrDefault()?.Name)
? new Studio(tv.ProductionCompanies.First().Name)
: null,
- IsMovie = true,
People = tv.Credits.Cast
.Select(x => x.ToPeople(provider))
.Concat(tv.Credits.Crew.Select(x => x.ToPeople(provider)))
@@ -84,7 +83,6 @@ namespace Kyoo.TheMovieDb
? $"https://image.tmdb.org/t/p/original{tv.BackdropPath}"
: null,
},
- IsMovie = true,
ExternalIDs = new []
{
new MetadataID
diff --git a/Kyoo.TheMovieDb/ProviderTmdb.cs b/Kyoo.TheMovieDb/ProviderTmdb.cs
index f47b2ef8..96bbfb5a 100644
--- a/Kyoo.TheMovieDb/ProviderTmdb.cs
+++ b/Kyoo.TheMovieDb/ProviderTmdb.cs
@@ -122,12 +122,16 @@ namespace Kyoo.TheMovieDb
/// A season containing metadata from TheMovieDb
private async Task _GetSeason(Season season)
{
- if (season.Show == null || !season.Show.TryGetID(Provider.Slug, out int id))
+ if (season.Show == null)
{
_logger.LogWarning("Metadata for a season was requested but it's show is not loaded. " +
"This is unsupported");
return null;
}
+
+ if (!season.Show.TryGetID(Provider.Slug, out int id))
+ return null;
+
TMDbClient client = new(_apiKey.Value.ApiKey);
return (await client.GetTvSeasonAsync(id, season.SeasonNumber))
.ToSeason(id, Provider);
@@ -141,13 +145,14 @@ namespace Kyoo.TheMovieDb
/// An episode containing metadata from TheMovieDb
private async Task _GetEpisode(Episode episode)
{
- if (episode.Show == null || !episode.Show.TryGetID(Provider.Slug, out int id))
+ if (episode.Show == null)
{
- _logger.LogWarning("Metadata for a season was requested but it's show is not loaded. " +
+ _logger.LogWarning("Metadata for an episode was requested but it's show is not loaded. " +
"This is unsupported");
return null;
}
- if (episode.SeasonNumber == null || episode.EpisodeNumber == null)
+ if (!episode.Show.TryGetID(Provider.Slug, out int id)
+ || episode.SeasonNumber == null || episode.EpisodeNumber == null)
return null;
TMDbClient client = new(_apiKey.Value.ApiKey);
diff --git a/Kyoo/Controllers/ThumbnailsManager.cs b/Kyoo/Controllers/ThumbnailsManager.cs
index 61d14613..bd24d975 100644
--- a/Kyoo/Controllers/ThumbnailsManager.cs
+++ b/Kyoo/Controllers/ThumbnailsManager.cs
@@ -1,6 +1,7 @@
using Kyoo.Models;
using System;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using Kyoo.Models.Options;
using Microsoft.Extensions.Logging;
@@ -87,10 +88,13 @@ namespace Kyoo.Controllers
if (item == null)
throw new ArgumentNullException(nameof(item));
+ if (item.Images == null)
+ return false;
+
string name = item is IResource res ? res.Slug : "???";
bool ret = false;
- foreach ((int id, string image) in item.Images)
+ foreach ((int id, string image) in item.Images.Where(x => x.Value != null))
{
string localPath = await GetImagePath(item, id);
if (alwaysDownload || !await _files.Exists(localPath))
diff --git a/Kyoo/Tasks/RegisterEpisode.cs b/Kyoo/Tasks/RegisterEpisode.cs
index 033bfceb..1876d9c5 100644
--- a/Kyoo/Tasks/RegisterEpisode.cs
+++ b/Kyoo/Tasks/RegisterEpisode.cs
@@ -116,9 +116,6 @@ namespace Kyoo.Tasks
else
show = registeredShow;
- // If they are not already loaded, load external ids to allow metadata providers to use them.
- if (show.ExternalIDs == null)
- await _libraryManager.Load(show, x => x.ExternalIDs);
progress.Report(50);
if (season != null)
@@ -163,14 +160,18 @@ namespace Kyoo.Tasks
/// The type of the item
/// The existing or filled item.
private async Task _RegisterAndFill(T item)
- where T : class, IResource, IThumbnails
+ where T : class, IResource, IThumbnails, IMetadata
{
if (item == null || string.IsNullOrEmpty(item.Slug))
return null;
T existing = await _libraryManager.GetOrDefault(item.Slug);
if (existing != null)
+ {
+ await _libraryManager.Load(existing, x => x.ExternalIDs);
return existing;
+ }
+
item = await _metadataProvider.Get(item);
await _thumbnailsManager.DownloadImages(item);
return await _libraryManager.CreateIfNotExists(item);