diff --git a/Shoko.Server/API/v3/Controllers/ActionController.cs b/Shoko.Server/API/v3/Controllers/ActionController.cs index 137702115..6b31cbf96 100644 --- a/Shoko.Server/API/v3/Controllers/ActionController.cs +++ b/Shoko.Server/API/v3/Controllers/ActionController.cs @@ -192,6 +192,16 @@ public ActionResult UpdateAllTmdbShows() return Ok(); } + /// + /// Download any missing TMDB People. + /// + [HttpGet("DownloadMissingTmdbPeople")] + public ActionResult DownloadMissingTmdbPeople() + { + Task.Factory.StartNew(() => _tmdbService.RepairMissingPeople()); + return Ok(); + } + /// /// Purge all unused TMDB Shows that are not linked to any AniDB anime. /// diff --git a/Shoko.Server/Databases/DatabaseFixes.cs b/Shoko.Server/Databases/DatabaseFixes.cs index 80007142e..02b52083f 100644 --- a/Shoko.Server/Databases/DatabaseFixes.cs +++ b/Shoko.Server/Databases/DatabaseFixes.cs @@ -837,4 +837,10 @@ public static void ClearQuartzQueue() var queueHandler = Utils.ServiceContainer.GetRequiredService(); queueHandler.Clear().ConfigureAwait(false).GetAwaiter().GetResult(); } + + public static void RepairMissingTMDBPersons() + { + var service = Utils.ServiceContainer.GetRequiredService(); + service.RepairMissingPeople().ConfigureAwait(false).GetAwaiter().GetResult(); + } } diff --git a/Shoko.Server/Databases/MySQL.cs b/Shoko.Server/Databases/MySQL.cs index 588499569..c981f79b7 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; } = 139; + public override int RequiredVersion { get; } = 140; private List createVersionTable = new() { @@ -849,6 +849,7 @@ 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) }; private DatabaseCommand linuxTableVersionsFix = new("RENAME TABLE versions TO Versions;"); diff --git a/Shoko.Server/Databases/SQLServer.cs b/Shoko.Server/Databases/SQLServer.cs index 45aa05dc7..855cf5afd 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; } = 131; + public override int RequiredVersion { get; } = 132; public override void BackupDatabase(string fullfilename) { @@ -779,6 +779,7 @@ 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) }; private static void AlterImdbMovieIDType() diff --git a/Shoko.Server/Databases/SQLite.cs b/Shoko.Server/Databases/SQLite.cs index fa3661948..2a9f76f9c 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 => 123; + public override int RequiredVersion => 124; public override void BackupDatabase(string fullfilename) { @@ -774,6 +774,7 @@ 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) }; private static Tuple MigrateRenamers(object connection) diff --git a/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs b/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs index 19a66e06b..16b4354e5 100644 --- a/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs +++ b/Shoko.Server/Providers/TMDB/TmdbMetadataService.cs @@ -2031,6 +2031,39 @@ private void PurgeCompany(int companyId, bool removeImageFiles = true) #region People + public async Task RepairMissingPeople() + { + var missingIds = new HashSet(); + var updateCount = 0; + var skippedCount = 0; + + var peopleIds = _tmdbPeople.GetAll().Select(person => person.TmdbPersonID).ToHashSet(); + + foreach (var person in _tmdbEpisodeCast.GetAll()) + if (!peopleIds.Contains(person.TmdbPersonID)) missingIds.Add(person.TmdbPersonID); + foreach (var person in _tmdbEpisodeCrew.GetAll()) + if (!peopleIds.Contains(person.TmdbPersonID)) missingIds.Add(person.TmdbPersonID); + + foreach (var person in _tmdbMovieCast.GetAll()) + if (!peopleIds.Contains(person.TmdbPersonID)) missingIds.Add(person.TmdbPersonID); + foreach (var person in _tmdbMovieCrew.GetAll()) + if (!peopleIds.Contains(person.TmdbPersonID)) missingIds.Add(person.TmdbPersonID); + + _logger.LogDebug("Found {@Count} unique missing TMDB People for Episode & Movie staff", missingIds.Count); + + foreach (var personId in missingIds) + { + var (_, updated) = await UpdatePerson(personId, forceRefresh: true); + if (updated) + updateCount++; + else + skippedCount++; + } + + _logger.LogInformation("Updated missing TMDB People: Found/Updated/Skipped {@Found}/{@Updated}/{@Skipped}", + missingIds.Count, updateCount, skippedCount); + } + public async Task<(bool added, bool updated)> UpdatePerson(int personId, bool forceRefresh = false, bool downloadImages = false) { using (await GetLockForEntity(ForeignEntityType.Person, personId, "metadata & images", "Update").ConfigureAwait(false)) @@ -2048,6 +2081,12 @@ private void PurgeCompany(int companyId, bool removeImageFiles = true) methods |= PersonMethods.Images; var newlyAdded = tmdbPerson.TMDB_PersonID is 0; var person = await UseClient(c => c.GetPersonAsync(personId, methods), $"Get person {personId}"); + if (person is null) + { + _logger.LogDebug("Unable to update staff; Scheduling refresh of related links. (Person={PersonId})", personId); + await ScheduleUpdateMoviesAndShowsByPerson(personId); + return (false, false); + } var updated = tmdbPerson.Populate(person); if (updated) { @@ -2062,6 +2101,28 @@ private void PurgeCompany(int companyId, bool removeImageFiles = true) } } + private async Task ScheduleUpdateMoviesAndShowsByPerson(int tmdbPersonId) + { + var showIds = new HashSet(); + var movieIds = new HashSet(); + + foreach (var staff in _tmdbEpisodeCast.GetByTmdbPersonID(tmdbPersonId)) + showIds.Add(staff.TmdbShowID); + foreach (var staff in _tmdbEpisodeCrew.GetByTmdbPersonID(tmdbPersonId)) + showIds.Add(staff.TmdbShowID); + + foreach (var staff in _tmdbMovieCast.GetByTmdbPersonID(tmdbPersonId)) + movieIds.Add(staff.TmdbMovieID); + foreach (var staff in _tmdbMovieCrew.GetByTmdbPersonID(tmdbPersonId)) + movieIds.Add(staff.TmdbMovieID); + + foreach (var showId in showIds) + await ScheduleUpdateOfShow(showId, downloadCrewAndCast: true, forceRefresh: true); + + foreach (var movieId in movieIds) + await ScheduleUpdateOfMovie(movieId, downloadCrewAndCast: true, forceRefresh: true); + } + private async Task DownloadPersonImages(int personId, ProfileImages images, bool forceDownload = false) { var settings = _settingsProvider.GetSettings();