diff --git a/Shoko.Server/API/v3/Controllers/TmdbController.cs b/Shoko.Server/API/v3/Controllers/TmdbController.cs index 60fc6e1ca..4f4a278d7 100644 --- a/Shoko.Server/API/v3/Controllers/TmdbController.cs +++ b/Shoko.Server/API/v3/Controllers/TmdbController.cs @@ -317,6 +317,31 @@ public ActionResult> GetContentRatingsForTmdbMovieB return new(movie.ContentRatings.ToDto(language)); } + [HttpGet("Movie/{movieID}/Keywords")] + public ActionResult> GetKeywordsForTmdbMovieByMovieID( + [FromRoute] int movieID + ) + { + var movie = RepoFactory.TMDB_Movie.GetByTmdbMovieID(movieID); + if (movie is null) + return NotFound(MovieNotFound); + + return movie.Keywords; + } + + [HttpGet("Movie/{movieID}/ProductionCountries")] + public ActionResult> GetProductionCountriesForTmdbMovieByMovieID( + [FromRoute] int movieID + ) + { + var movie = RepoFactory.TMDB_Movie.GetByTmdbMovieID(movieID); + if (movie is null) + return NotFound(MovieNotFound); + + return movie.ProductionCountries + .ToDictionary(country => country.CountryCode, country => country.CountryName); + } + #endregion #region Same-Source Linked Entries @@ -1144,6 +1169,31 @@ public ActionResult> GetContentRatingsForTmdbShowBy return new(show.ContentRatings.ToDto(language)); } + [HttpGet("Show/{showID}/Keywords")] + public ActionResult> GetKeywordsForTmdbShowByShowID( + [FromRoute] int showID + ) + { + var show = RepoFactory.TMDB_Show.GetByTmdbShowID(showID); + if (show is null) + return NotFound(ShowNotFound); + + return show.Keywords; + } + + [HttpGet("Show/{showID}/ProductionCountries")] + public ActionResult> GetProductionCountriesForTmdbShowByShowID( + [FromRoute] int showID + ) + { + var show = RepoFactory.TMDB_Show.GetByTmdbShowID(showID); + if (show is null) + return NotFound(ShowNotFound); + + return show.ProductionCountries + .ToDictionary(country => country.CountryCode, country => country.CountryName); + } + #endregion #region Same-Source Linked Entries diff --git a/Shoko.Server/API/v3/Models/TMDB/Movie.cs b/Shoko.Server/API/v3/Models/TMDB/Movie.cs index 5116b9f57..f9e1b84d8 100644 --- a/Shoko.Server/API/v3/Models/TMDB/Movie.cs +++ b/Shoko.Server/API/v3/Models/TMDB/Movie.cs @@ -101,6 +101,12 @@ public class Movie /// public IReadOnlyList Genres { get; init; } + /// + /// Keywords. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList? Keywords { get; init; } + /// /// Content ratings for different countries for this show. /// @@ -113,6 +119,12 @@ public class Movie [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IReadOnlyList? Studios { get; init; } + /// + /// Production countries. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyDictionary? ProductionCountries { get; init; } + /// /// Images associated with the movie, if they should be included. /// @@ -217,6 +229,11 @@ public Movie(TMDB_Movie movie, IncludeDetails? includeDetails = null, IReadOnlyS .ToList(); if (include.HasFlag(IncludeDetails.FileCrossReferences)) FileCrossReferences = FileCrossReference.From(movie.FileCrossReferences); + if (include.HasFlag(IncludeDetails.Keywords)) + Keywords = movie.Keywords; + if (include.HasFlag(IncludeDetails.ProductionCountries)) + ProductionCountries = movie.ProductionCountries + .ToDictionary(country => country.CountryCode, country => country.CountryName); ReleasedAt = movie.ReleasedAt; CreatedAt = movie.CreatedAt.ToUniversalTime(); LastUpdatedAt = movie.LastUpdatedAt.ToUniversalTime(); @@ -356,5 +373,7 @@ public enum IncludeDetails Studios = 64, ContentRatings = 128, FileCrossReferences = 256, + Keywords = 512, + ProductionCountries = 1024, } } diff --git a/Shoko.Server/API/v3/Models/TMDB/Show.cs b/Shoko.Server/API/v3/Models/TMDB/Show.cs index 7ee649e38..b18c6f7a0 100644 --- a/Shoko.Server/API/v3/Models/TMDB/Show.cs +++ b/Shoko.Server/API/v3/Models/TMDB/Show.cs @@ -73,6 +73,12 @@ public class Show /// public IReadOnlyList Genres { get; init; } + /// + /// Keywords. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList? Keywords { get; init; } + /// /// Content ratings for different countries for this show. /// @@ -85,6 +91,12 @@ public class Show [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IReadOnlyList? Studios { get; init; } + /// + /// Production countries. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyDictionary? ProductionCountries { get; init; } + /// /// The television networks that aired the show. /// @@ -240,6 +252,11 @@ public Show(TMDB_Show show, TMDB_AlternateOrdering? alternateOrdering, IncludeDe .OrderBy(xref => xref.AnidbAnimeID) .ThenBy(xref => xref.TmdbShowID) .ToList(); + if (include.HasFlag(IncludeDetails.Keywords)) + Keywords = show.Keywords; + if (include.HasFlag(IncludeDetails.ProductionCountries)) + ProductionCountries = show.ProductionCountries + .ToDictionary(country => country.CountryCode, country => country.CountryName); FirstAiredAt = show.FirstAiredAt; LastAiredAt = show.LastAiredAt; CreatedAt = show.CreatedAt.ToUniversalTime(); @@ -358,5 +375,7 @@ public enum IncludeDetails Studios = 128, Networks = 256, ContentRatings = 512, + Keywords = 1024, + ProductionCountries = 2048, } } diff --git a/Shoko.Server/Databases/MySQL.cs b/Shoko.Server/Databases/MySQL.cs index c981f79b7..377f0b820 100644 --- a/Shoko.Server/Databases/MySQL.cs +++ b/Shoko.Server/Databases/MySQL.cs @@ -27,7 +27,7 @@ namespace Shoko.Server.Databases; public class MySQL : BaseDatabase { public override string Name { get; } = "MySQL"; - public override int RequiredVersion { get; } = 140; + public override int RequiredVersion { get; } = 141; private List createVersionTable = new() { @@ -849,7 +849,11 @@ public class MySQL : BaseDatabase new(139, 10, "ALTER TABLE Trakt_Show ADD COLUMN TmdbShowID INT NULL;"), new(139, 11, DatabaseFixes.CleanupAfterRemovingTvDB), new(139, 12, DatabaseFixes.ClearQuartzQueue), - new(140, 1, DatabaseFixes.RepairMissingTMDBPersons) + new(140, 1, DatabaseFixes.RepairMissingTMDBPersons), + new(141, 1, "ALTER TABLE `TMDB_Movie` ADD COLUMN `Keywords` VARCHAR(512) NULL DEFAULT NULL;"), + new(141, 2, "ALTER TABLE `TMDB_Movie` ADD COLUMN `ProductionCountries` VARCHAR(32) NULL DEFAULT NULL;"), + new(141, 3, "ALTER TABLE `TMDB_Show` ADD COLUMN `Keywords` VARCHAR(512) NULL DEFAULT NULL;"), + new(141, 4, "ALTER TABLE `TMDB_Show` ADD COLUMN `ProductionCountries` VARCHAR(32) NULL DEFAULT NULL;"), }; private DatabaseCommand linuxTableVersionsFix = new("RENAME TABLE versions TO Versions;"); diff --git a/Shoko.Server/Databases/NHIbernate/StringListConverter.cs b/Shoko.Server/Databases/NHIbernate/StringListConverter.cs index 69e1cac3e..098d8f98b 100644 --- a/Shoko.Server/Databases/NHIbernate/StringListConverter.cs +++ b/Shoko.Server/Databases/NHIbernate/StringListConverter.cs @@ -36,6 +36,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) => value switch { + null => [], string i => i.Split("|||").ToList(), List l => l, _ => throw new ArgumentException($"DestinationType must be {nameof(String)}.") @@ -44,6 +45,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type? destinationType) => value switch { + null => string.Empty, string i => i, List l => l.Join("|||"), _ => throw new ArgumentException($"DestinationType must be {typeof(List).FullName}."), diff --git a/Shoko.Server/Databases/NHIbernate/TmdbContentRatingConverter.cs b/Shoko.Server/Databases/NHIbernate/TmdbContentRatingConverter.cs index 0cba0cfbd..7b85c2f2f 100644 --- a/Shoko.Server/Databases/NHIbernate/TmdbContentRatingConverter.cs +++ b/Shoko.Server/Databases/NHIbernate/TmdbContentRatingConverter.cs @@ -36,6 +36,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) => value switch { + null => [], string i => i.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(s => TMDB_ContentRating.FromString(s)).ToList(), List l => l, _ => throw new ArgumentException($"DestinationType must be {nameof(String)}.") @@ -44,6 +45,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type? destinationType) => value switch { + null => string.Empty, string i => i, List l => l.Select(r => r.ToString()).Join('|'), _ => throw new ArgumentException($"DestinationType must be {typeof(List).FullName}."), diff --git a/Shoko.Server/Databases/NHIbernate/TmdbProductionCountryConverter.cs b/Shoko.Server/Databases/NHIbernate/TmdbProductionCountryConverter.cs new file mode 100644 index 000000000..b50ea2677 --- /dev/null +++ b/Shoko.Server/Databases/NHIbernate/TmdbProductionCountryConverter.cs @@ -0,0 +1,93 @@ +using System; +using System.ComponentModel; +using NHibernate.SqlTypes; +using NHibernate.UserTypes; +using System.Data; +using System.Data.Common; +using NHibernate; +using NHibernate.Engine; +using System.Globalization; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Shoko.Server.Extensions; +using Shoko.Server.Models.TMDB; + +#nullable enable +namespace Shoko.Server.Databases.NHibernate; + +public class TmdbProductionCountryConverter : TypeConverter, IUserType +{ + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType) + => sourceType?.FullName switch + { + nameof(List) => true, + nameof(String) => true, + _ => false + }; + + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) + => destinationType?.FullName switch + { + nameof(String) => true, + _ => false, + }; + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) + => value switch + { + null => [], + string i => i.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(s => TMDB_ProductionCountry.FromString(s)).ToList(), + List l => l, + _ => throw new ArgumentException($"DestinationType must be {nameof(String)}.") + }; + + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type? destinationType) + => value switch + { + null => string.Empty, + string i => i, + List l => l.Select(r => r.ToString()).Join('|'), + _ => throw new ArgumentException($"DestinationType must be {typeof(List).FullName}."), + }; + + public override object CreateInstance(ITypeDescriptorContext? context, IDictionary? propertyValues) + => true; + + #region IUserType Members + + public object Assemble(object cached, object owner) + => DeepCopy(cached); + + public object DeepCopy(object value) + => value; + + public object Disassemble(object value) + => DeepCopy(value); + + public int GetHashCode(object x) + => x == null ? base.GetHashCode() : x.GetHashCode(); + + public bool IsMutable + => true; + + public object? NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor impl, object owner) + => ConvertFrom(null, null, NHibernateUtil.String.NullSafeGet(rs, names[0], impl)); + + public void NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session) + => ((IDataParameter)cmd.Parameters[index]).Value = value == null ? DBNull.Value : ConvertTo(null, null, value, typeof(List)); + + public object Replace(object original, object target, object owner) + => original; + + public Type ReturnedType + => typeof(List); + + public SqlType[] SqlTypes + => new[] { NHibernateUtil.String.SqlType }; + + bool IUserType.Equals(object x, object y) + => ReferenceEquals(x, y) || (x != null && y != null && x.Equals(y)); + + #endregion +} diff --git a/Shoko.Server/Databases/SQLServer.cs b/Shoko.Server/Databases/SQLServer.cs index 855cf5afd..0faf84247 100644 --- a/Shoko.Server/Databases/SQLServer.cs +++ b/Shoko.Server/Databases/SQLServer.cs @@ -28,7 +28,7 @@ namespace Shoko.Server.Databases; public class SQLServer : BaseDatabase { public override string Name { get; } = "SQLServer"; - public override int RequiredVersion { get; } = 132; + public override int RequiredVersion { get; } = 133; public override void BackupDatabase(string fullfilename) { @@ -779,7 +779,11 @@ public override bool HasVersionsTable() new DatabaseCommand(131, 10, "ALTER TABLE Trakt_Show ADD TmdbShowID INT NULL;"), new DatabaseCommand(131, 11, DatabaseFixes.CleanupAfterRemovingTvDB), new DatabaseCommand(131, 12, DatabaseFixes.ClearQuartzQueue), - new DatabaseCommand(132, 1, DatabaseFixes.RepairMissingTMDBPersons) + new DatabaseCommand(132, 1, DatabaseFixes.RepairMissingTMDBPersons), + new DatabaseCommand(133, 1, "ALTER TABLE TMDB_Movie ADD Keywords NVARCHAR(512) NULL DEFAULT NULL;"), + new DatabaseCommand(133, 2, "ALTER TABLE TMDB_Movie ADD ProductionCountries NVARCHAR(32) NULL DEFAULT NULL;"), + new DatabaseCommand(133, 3, "ALTER TABLE TMDB_Show ADD Keywords NVARCHAR(512) NULL DEFAULT NULL;"), + new DatabaseCommand(133, 4, "ALTER TABLE TMDB_Show ADD ProductionCountries NVARCHAR(32) NULL DEFAULT NULL;"), }; private static void AlterImdbMovieIDType() diff --git a/Shoko.Server/Databases/SQLite.cs b/Shoko.Server/Databases/SQLite.cs index 2a9f76f9c..2e423c3f9 100644 --- a/Shoko.Server/Databases/SQLite.cs +++ b/Shoko.Server/Databases/SQLite.cs @@ -28,7 +28,7 @@ public class SQLite : BaseDatabase { public override string Name => "SQLite"; - public override int RequiredVersion => 124; + public override int RequiredVersion => 125; public override void BackupDatabase(string fullfilename) { @@ -774,7 +774,11 @@ public override void CreateDatabase() new(123, 10, "ALTER TABLE Trakt_Show ADD COLUMN TmdbShowID INTEGER NULL;"), new(123, 11, DatabaseFixes.CleanupAfterRemovingTvDB), new(123, 12, DatabaseFixes.ClearQuartzQueue), - new(124, 1, DatabaseFixes.RepairMissingTMDBPersons) + new(124, 1, DatabaseFixes.RepairMissingTMDBPersons), + new(125, 1, "ALTER TABLE TMDB_Movie ADD COLUMN Keywords TEXT NULL DEFAULT NULL;"), + new(125, 2, "ALTER TABLE TMDB_Movie ADD COLUMN ProductionCountries TEXT NULL DEFAULT NULL;"), + new(125, 3, "ALTER TABLE TMDB_Show ADD COLUMN Keywords TEXT NULL DEFAULT NULL;"), + new(125, 4, "ALTER TABLE TMDB_Show ADD COLUMN ProductionCountries TEXT NULL DEFAULT NULL;"), }; private static Tuple MigrateRenamers(object connection) diff --git a/Shoko.Server/Mappings/TMDB/TMDB_MovieMap.cs b/Shoko.Server/Mappings/TMDB/TMDB_MovieMap.cs index fa364a491..764c707d9 100644 --- a/Shoko.Server/Mappings/TMDB/TMDB_MovieMap.cs +++ b/Shoko.Server/Mappings/TMDB/TMDB_MovieMap.cs @@ -25,7 +25,9 @@ public TMDB_MovieMap() Map(x => x.IsRestricted).Not.Nullable(); Map(x => x.IsVideo).Not.Nullable(); Map(x => x.Genres).Not.Nullable().CustomType(); + Map(x => x.Keywords).Not.Nullable().CustomType(); Map(x => x.ContentRatings).Not.Nullable().CustomType(); + Map(x => x.ProductionCountries).Not.Nullable().CustomType(); Map(x => x.RuntimeMinutes).Column("Runtime"); Map(x => x.UserRating).Not.Nullable(); Map(x => x.UserVotes).Not.Nullable(); diff --git a/Shoko.Server/Mappings/TMDB/TMDB_ShowMap.cs b/Shoko.Server/Mappings/TMDB/TMDB_ShowMap.cs index 0d76bf8bc..694610275 100644 --- a/Shoko.Server/Mappings/TMDB/TMDB_ShowMap.cs +++ b/Shoko.Server/Mappings/TMDB/TMDB_ShowMap.cs @@ -23,7 +23,9 @@ public TMDB_ShowMap() Map(x => x.OriginalLanguageCode).Not.Nullable(); Map(x => x.IsRestricted).Not.Nullable(); Map(x => x.Genres).Not.Nullable().CustomType(); + Map(x => x.Keywords).Not.Nullable().CustomType(); Map(x => x.ContentRatings).Not.Nullable().CustomType(); + Map(x => x.ProductionCountries).Not.Nullable().CustomType(); Map(x => x.EpisodeCount).Not.Nullable(); Map(x => x.SeasonCount).Not.Nullable(); Map(x => x.AlternateOrderingCount).Not.Nullable(); diff --git a/Shoko.Server/Models/TMDB/Embedded/TMDB_ProductionCountry.cs b/Shoko.Server/Models/TMDB/Embedded/TMDB_ProductionCountry.cs new file mode 100644 index 000000000..6ba17407f --- /dev/null +++ b/Shoko.Server/Models/TMDB/Embedded/TMDB_ProductionCountry.cs @@ -0,0 +1,43 @@ +using System; +using Shoko.Server.Extensions; + +#nullable enable +namespace Shoko.Server.Models.TMDB; + +[Serializable] +public class TMDB_ProductionCountry +{ + /// + /// ISO-3166 alpha-2 country code. + /// + public string CountryCode { get; set; } + + /// + /// English country name. + /// + public string CountryName { get; set; } + + public TMDB_ProductionCountry() + { + CountryCode = string.Empty; + CountryName = string.Empty; + } + + public TMDB_ProductionCountry(string countryCode, string countryName) + { + CountryCode = countryCode; + CountryName = countryName; + } + + public override string ToString() + { + return $"{CountryCode},{CountryName}"; + } + + public static TMDB_ProductionCountry FromString(string str) + { + var (countryCode, countryName) = str.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + return new(countryCode, countryName); + } + +} diff --git a/Shoko.Server/Models/TMDB/TMDB_Movie.cs b/Shoko.Server/Models/TMDB/TMDB_Movie.cs index 86c632139..eccdaa75f 100644 --- a/Shoko.Server/Models/TMDB/TMDB_Movie.cs +++ b/Shoko.Server/Models/TMDB/TMDB_Movie.cs @@ -125,11 +125,21 @@ public TitleLanguage OriginalLanguage /// public List Genres { get; set; } = []; + /// + /// Keywords / Tags. + /// + public List Keywords { get; set; } = []; + /// /// Content ratings for different countries for this movie. /// public List ContentRatings { get; set; } = []; + /// + /// Production countries. + /// + public List ProductionCountries { get; set; } = []; + /// /// Movie run-time in minutes. /// @@ -223,6 +233,7 @@ public bool Populate(Movie movie, HashSet? crLanguages) UpdateProperty(IsRestricted, movie.Adult, v => IsRestricted = v), UpdateProperty(IsVideo, movie.Video, v => IsVideo = v), UpdateProperty(Genres, movie.GetGenres(), v => Genres = v, (a, b) => string.Equals(string.Join("|", a), string.Join("|", b))), + UpdateProperty(Keywords, movie.Keywords.Keywords.Select(k => k.Name).ToList(), v => Keywords = v, (a, b) => string.Equals(string.Join("|", a), string.Join("|", b))), UpdateProperty( ContentRatings, movie.ReleaseDates.Results @@ -234,6 +245,15 @@ public bool Populate(Movie movie, HashSet? crLanguages) v => ContentRatings = v, (a, b) => string.Equals(string.Join(",", a.Select(a1 => a1.ToString())), string.Join(",", b.Select(b1 => b1.ToString()))) ), + UpdateProperty( + ProductionCountries, + movie.ProductionCountries + .Select(country => new TMDB_ProductionCountry(country.Iso_3166_1, country.Name)) + .OrderBy(c => c.CountryCode) + .ToList(), + v => ProductionCountries = v, + (a, b) => string.Equals(string.Join(",", a.Select(a1 => a1.ToString())), string.Join(",", b.Select(b1 => b1.ToString()))) + ), UpdateProperty(Runtime, movie.Runtime.HasValue ? TimeSpan.FromMinutes(movie.Runtime.Value) : null, v => Runtime = v), UpdateProperty(UserRating, movie.VoteAverage, v => UserRating = v), UpdateProperty(UserVotes, movie.VoteCount, v => UserVotes = v), diff --git a/Shoko.Server/Models/TMDB/TMDB_Show.cs b/Shoko.Server/Models/TMDB/TMDB_Show.cs index 983423667..e40df9a0f 100644 --- a/Shoko.Server/Models/TMDB/TMDB_Show.cs +++ b/Shoko.Server/Models/TMDB/TMDB_Show.cs @@ -102,11 +102,21 @@ public TitleLanguage OriginalLanguage /// public List Genres { get; set; } = []; + /// + /// Keywords / Tags. + /// + public List Keywords { get; set; } = []; + /// /// Content ratings for different countries for this show. /// public List ContentRatings { get; set; } = []; + /// + /// Production countries. + /// + public List ProductionCountries { get; set; } = []; + /// /// Number of episodes using the default ordering. /// @@ -209,6 +219,7 @@ public bool Populate(TvShow show, HashSet? crLanguages) UpdateProperty(EnglishOverview, !string.IsNullOrEmpty(translation?.Data.Overview) ? translation.Data.Overview : show.Overview, v => EnglishOverview = v), UpdateProperty(IsRestricted, show.Adult, v => IsRestricted = v), UpdateProperty(Genres, show.GetGenres(), v => Genres = v, (a, b) => string.Equals(string.Join("|", a), string.Join("|", b))), + UpdateProperty(Keywords, show.Keywords.Results.Select(k => k.Name).ToList(), v => Keywords = v, (a, b) => string.Equals(string.Join("|", a), string.Join("|", b))), UpdateProperty( ContentRatings, show.ContentRatings.Results @@ -219,6 +230,15 @@ public bool Populate(TvShow show, HashSet? crLanguages) v => ContentRatings = v, (a, b) => string.Equals(string.Join(",", a.Select(a1 => a1.ToString())), string.Join(",", b.Select(b1 => b1.ToString()))) ), + UpdateProperty( + ProductionCountries, + show.ProductionCountries + .Select(country => new TMDB_ProductionCountry(country.Iso_3166_1, country.Name)) + .OrderBy(c => c.CountryCode) + .ToList(), + v => ProductionCountries = v, + (a, b) => string.Equals(string.Join(",", a.Select(a1 => a1.ToString())), string.Join(",", b.Select(b1 => b1.ToString()))) + ), UpdateProperty(EpisodeCount, show.NumberOfEpisodes, v => EpisodeCount = v), UpdateProperty(SeasonCount, show.NumberOfSeasons, v => SeasonCount = v), UpdateProperty(AlternateOrderingCount, show.EpisodeGroups?.Results.Count ?? AlternateOrderingCount, v => AlternateOrderingCount = v), diff --git a/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs b/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs index 279b43b20..0b90e35bc 100644 --- a/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs +++ b/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs @@ -434,7 +434,7 @@ public async Task UpdateMovie(int movieId, bool forceRefresh = false, bool } // Abort if we couldn't find the movie by id. - var methods = MovieMethods.Translations | MovieMethods.ReleaseDates | MovieMethods.ExternalIds; + var methods = MovieMethods.Translations | MovieMethods.ReleaseDates | MovieMethods.ExternalIds | MovieMethods.Keywords; if (downloadCrewAndCast) methods |= MovieMethods.Credits; var movie = await UseClient(c => c.GetMovieAsync(movieId, "en-US", null, methods), $"Get movie {movieId}").ConfigureAwait(false); @@ -965,7 +965,7 @@ public async Task UpdateShow(int showId, bool forceRefresh = false, bool d return false; } - var methods = TvShowMethods.ContentRatings | TvShowMethods.Translations | TvShowMethods.ExternalIds; + var methods = TvShowMethods.ContentRatings | TvShowMethods.Translations | TvShowMethods.ExternalIds | TvShowMethods.Keywords; if (downloadAlternateOrdering && !quickRefresh) methods |= TvShowMethods.EpisodeGroups; var show = await UseClient(c => c.GetTvShowAsync(showId, methods, "en-US"), $"Get Show {showId}").ConfigureAwait(false);