diff --git a/internal/catalog/item_repo.go b/internal/catalog/item_repo.go index 39dfcfad..c2ebcb23 100644 --- a/internal/catalog/item_repo.go +++ b/internal/catalog/item_repo.go @@ -1389,9 +1389,9 @@ func lookupExternalIDsSQL() string { JOIN media_items mi ON mi.type = $5 AND ( - (r.provider = 'tmdb' AND mi.tmdb_id = r.provider_id) - OR (r.provider = 'tvdb' AND mi.tvdb_id = r.provider_id) - OR (r.provider = 'imdb' AND mi.imdb_id = r.provider_id) + (r.provider = 'tmdb' AND mi.tmdb_id <> '' AND mi.tmdb_id = r.provider_id) + OR (r.provider = 'tvdb' AND mi.tvdb_id <> '' AND mi.tvdb_id = r.provider_id) + OR (r.provider = 'imdb' AND mi.imdb_id <> '' AND mi.imdb_id = r.provider_id) ) JOIN media_item_libraries mil ON mil.content_id = mi.content_id JOIN media_folders mf ON mf.id = mil.media_folder_id diff --git a/internal/catalog/item_repo_test.go b/internal/catalog/item_repo_test.go index 3e7d8ad8..b8c396ad 100644 --- a/internal/catalog/item_repo_test.go +++ b/internal/catalog/item_repo_test.go @@ -199,9 +199,11 @@ func TestLookupExternalIDsSQLChecksProviderTableAndDirectColumns(t *testing.T) { "JOIN media_item_provider_ids mip", "mip.provider = r.provider", "mip.provider_id = r.provider_id", - "mi.tmdb_id = r.provider_id", - "mi.tvdb_id = r.provider_id", - "mi.imdb_id = r.provider_id", + "mip.item_type = $5", + "mi.type = $5", + "mi.tmdb_id <> '' AND mi.tmdb_id = r.provider_id", + "mi.tvdb_id <> '' AND mi.tvdb_id = r.provider_id", + "mi.imdb_id <> '' AND mi.imdb_id = r.provider_id", "JOIN media_folders mf ON mf.id = mil.media_folder_id", "mf.enabled = true", } { diff --git a/internal/catalog/provider_id_repo.go b/internal/catalog/provider_id_repo.go index 264a6ae8..6022f846 100644 --- a/internal/catalog/provider_id_repo.go +++ b/internal/catalog/provider_id_repo.go @@ -70,6 +70,36 @@ func (r *ProviderIDRepository) AttachTMDBID(ctx context.Context, contentID, item return fmt.Errorf("media item tmdb id conflict: got %q, want %q", existingTMDBID, tmdbText) } + var existingProviderTMDBID string + err = tx.QueryRow(ctx, ` + SELECT provider_id + FROM media_item_provider_ids + WHERE content_id = $1 AND provider = 'tmdb' + FOR UPDATE + `, contentID).Scan(&existingProviderTMDBID) + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return fmt.Errorf("loading media item tmdb provider id: %w", err) + } + if existingProviderTMDBID != "" && existingProviderTMDBID != tmdbText { + return fmt.Errorf("media item tmdb provider id conflict: got %q, want %q", existingProviderTMDBID, tmdbText) + } + + var existingOwnerContentID string + err = tx.QueryRow(ctx, ` + SELECT content_id + FROM media_items + WHERE type = $1 + AND tmdb_id = $2 + AND content_id <> $3 + LIMIT 1 + `, itemType, tmdbText, contentID).Scan(&existingOwnerContentID) + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return fmt.Errorf("checking tmdb id owner: %w", err) + } + if existingOwnerContentID != "" { + return fmt.Errorf("tmdb id %q already belongs to content_id %q", tmdbText, existingOwnerContentID) + } + if _, err := tx.Exec(ctx, ` UPDATE media_items SET tmdb_id = $1,