Skip to content

fix: add DB migration & endpoint to download missing TMDB People #1203

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
12 changes: 12 additions & 0 deletions Shoko.Server/API/v3/Controllers/TmdbController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,18 @@ public async Task<ActionResult> DownloadImagesForTmdbShowByShowID(
return NoContent();
}

/// <summary>
/// Download any missing TMDB Persons.
/// </summary>
/// <param name="removeErrors">Removes all references to the bad TMDB Person if unable to successfully download the person</param>
/// <returns> <see cref="OkResult"/> with a summary of found/updated/skipped/deleted actions. </returns>
[HttpGet("Person/DownloadMissing")]
public async Task<ActionResult> RepairMissingTmdbPersons([FromQuery] bool removeErrors = false)
{
var result = await _tmdbMetadataService.RepairMissingPersons();
return Ok($"(Found/Updated/Skipped/Deleted) ({result.Found}/{result.Updated}/{result.Skipped}/{result.Removed}) missing TMDB person(s).");
}

#endregion

#region Online (Search / Bulk / Single)
Expand Down
6 changes: 6 additions & 0 deletions Shoko.Server/Databases/DatabaseFixes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,4 +837,10 @@ public static void ClearQuartzQueue()
var queueHandler = Utils.ServiceContainer.GetRequiredService<QueueHandler>();
queueHandler.Clear().ConfigureAwait(false).GetAwaiter().GetResult();
}

public static void RepairMissingTMDBPersons()
{
var service = Utils.ServiceContainer.GetRequiredService<TmdbMetadataService>();
service.RepairMissingPersons(true).ConfigureAwait(false).GetAwaiter().GetResult();
}
}
3 changes: 2 additions & 1 deletion Shoko.Server/Databases/MySQL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace Shoko.Server.Databases;
public class MySQL : BaseDatabase<MySqlConnection>
{
public override string Name { get; } = "MySQL";
public override int RequiredVersion { get; } = 139;
public override int RequiredVersion { get; } = 140;

private List<DatabaseCommand> createVersionTable = new()
{
Expand Down Expand Up @@ -849,6 +849,7 @@ public class MySQL : BaseDatabase<MySqlConnection>
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;");
Expand Down
3 changes: 2 additions & 1 deletion Shoko.Server/Databases/SQLServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Shoko.Server.Databases;
public class SQLServer : BaseDatabase<SqlConnection>
{
public override string Name { get; } = "SQLServer";
public override int RequiredVersion { get; } = 131;
public override int RequiredVersion { get; } = 132;

public override void BackupDatabase(string fullfilename)
{
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 2 additions & 1 deletion Shoko.Server/Databases/SQLite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class SQLite : BaseDatabase<SqliteConnection>
{
public override string Name => "SQLite";

public override int RequiredVersion => 123;
public override int RequiredVersion => 124;

public override void BackupDatabase(string fullfilename)
{
Expand Down Expand Up @@ -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<bool, string> MigrateRenamers(object connection)
Expand Down
48 changes: 46 additions & 2 deletions Shoko.Server/Providers/TMDB/TmdbMetadataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2031,6 +2031,50 @@ private void PurgeCompany(int companyId, bool removeImageFiles = true)

#region People

public async Task<(int Found, int Updated, int Skipped, int Removed)> RepairMissingPersons(bool removeErrors = false)
{
var missingIds = new HashSet<int>();
var (updateCount, skippedCount, removedCount) = (0, 0, 0);

var people = _tmdbPeople.GetAll().Select(person => person.TmdbPersonID).ToList();

_tmdbEpisodeCast.GetAll().AsParallel()
.Where(p => !people.Contains(p.TmdbPersonID))
.ForAll(p => missingIds.Add(p.TmdbPersonID));
_tmdbEpisodeCrew.GetAll().AsParallel()
.Where(p => !people.Contains(p.TmdbPersonID))
.ForAll(p => missingIds.Add(p.TmdbPersonID));

_tmdbMovieCast.GetAll().AsParallel()
.Where(p => !people.Contains(p.TmdbPersonID))
.ForAll(p => missingIds.Add(p.TmdbPersonID));
_tmdbMovieCrew.GetAll().AsParallel()
.Where(p => !people.Contains(p.TmdbPersonID))
.ForAll(p => missingIds.Add(p.TmdbPersonID));

foreach (var personId in missingIds)
{
try
{
await UpdatePerson(personId, forceRefresh: true);
updateCount++;
}
catch (NullReferenceException)
{
_logger.LogDebug("Unable to update TMDB Person ({@PersonId})", personId);

if (removeErrors && await PurgePerson(personId, removeImageFiles: true, forceRemoval: true))
{
removedCount++;
continue;
}
skippedCount++;
}
}

return (missingIds.Count, updateCount, skippedCount, removedCount);
}

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))
Expand Down Expand Up @@ -2072,11 +2116,11 @@ private async Task DownloadPersonImages(int personId, ProfileImages images, bool
await _imageService.DownloadImagesByType(images.Profiles, ImageEntityType.Person, ForeignEntityType.Person, personId, settings.TMDB.MaxAutoStaffImages, [], forceDownload);
}

public async Task<bool> PurgePerson(int personId, bool removeImageFiles = true)
public async Task<bool> PurgePerson(int personId, bool removeImageFiles = true, bool forceRemoval = false)
{
using (await GetLockForEntity(ForeignEntityType.Person, personId, "metadata & images", "Purge"))
{
if (IsPersonLinkedToOtherEntities(personId))
if (!forceRemoval && IsPersonLinkedToOtherEntities(personId))
return false;

var person = _tmdbPeople.GetByTmdbPersonID(personId);
Expand Down