diff --git a/internal/adminjob/library_refresh.go b/internal/adminjob/library_refresh.go index 5e2bc259..d92097c8 100644 --- a/internal/adminjob/library_refresh.go +++ b/internal/adminjob/library_refresh.go @@ -110,6 +110,13 @@ func (l *PGLibraryRefreshItemLister) ListLibraryItems(ctx context.Context, libra OR COALESCE(mi.logo_path, '') LIKE '%//logo/%' OR mi.refresh_failures > 0 OR mi.episode_metadata_incomplete = TRUE + OR ( + COALESCE(mi.tmdb_id, '') = '' + AND ( + COALESCE(mi.tvdb_id, '') <> '' + OR COALESCE(mi.imdb_id, '') <> '' + ) + ) OR EXISTS ( SELECT 1 FROM stale_media_ids smi diff --git a/internal/metadata/refresh_debt.go b/internal/metadata/refresh_debt.go index 0e57dc4b..3631c107 100644 --- a/internal/metadata/refresh_debt.go +++ b/internal/metadata/refresh_debt.go @@ -10,6 +10,7 @@ import ( const ( RefreshDebtReasonEpisodeIncomplete int64 = 1 << iota RefreshDebtReasonStaleProviderID + RefreshDebtReasonProviderIDIncomplete RefreshDebtReasonRefreshFailure RefreshDebtReasonCoreMetadataIncomplete ) @@ -43,6 +44,8 @@ func refreshDebtPriority(reasonMask int64) int { return 300 case hasRefreshDebtReason(reasonMask, RefreshDebtReasonStaleProviderID): return 250 + case hasRefreshDebtReason(reasonMask, RefreshDebtReasonProviderIDIncomplete): + return 240 case hasRefreshDebtReason(reasonMask, RefreshDebtReasonRefreshFailure): return 200 case hasRefreshDebtReason(reasonMask, RefreshDebtReasonCoreMetadataIncomplete): @@ -95,12 +98,25 @@ func refreshDebtReasonsForItem(item *models.MediaItem) int64 { if hasCoreMetadataRefreshDebt(item) { reasonMask |= RefreshDebtReasonCoreMetadataIncomplete } + if hasProviderIDRefreshDebt(item) { + reasonMask |= RefreshDebtReasonProviderIDIncomplete + } if item.RefreshFailures > 0 && strings.EqualFold(strings.TrimSpace(item.Status), "matched") { reasonMask |= RefreshDebtReasonRefreshFailure } return reasonMask } +func hasProviderIDRefreshDebt(item *models.MediaItem) bool { + if item == nil || !strings.EqualFold(strings.TrimSpace(item.Status), "matched") { + return false + } + if strings.TrimSpace(item.TmdbID) != "" { + return false + } + return strings.TrimSpace(item.TvdbID) != "" || strings.TrimSpace(item.ImdbID) != "" +} + func hasCoreMetadataRefreshDebt(item *models.MediaItem) bool { if item == nil || !strings.EqualFold(strings.TrimSpace(item.Status), "matched") { return false diff --git a/internal/metadata/refresh_debt_repo.go b/internal/metadata/refresh_debt_repo.go index 741b3712..de19aa9b 100644 --- a/internal/metadata/refresh_debt_repo.go +++ b/internal/metadata/refresh_debt_repo.go @@ -448,6 +448,7 @@ func (r *RefreshDebtRepository) GetMetrics(ctx context.Context, sampleLimit int) }{ {reason: "episode_incomplete", mask: RefreshDebtReasonEpisodeIncomplete}, {reason: "stale_provider_id", mask: RefreshDebtReasonStaleProviderID}, + {reason: "provider_id_incomplete", mask: RefreshDebtReasonProviderIDIncomplete}, {reason: "refresh_failure", mask: RefreshDebtReasonRefreshFailure}, {reason: "core_metadata_incomplete", mask: RefreshDebtReasonCoreMetadataIncomplete}, } diff --git a/internal/metadata/refresh_debt_test.go b/internal/metadata/refresh_debt_test.go index 64f197a7..97418bce 100644 --- a/internal/metadata/refresh_debt_test.go +++ b/internal/metadata/refresh_debt_test.go @@ -39,6 +39,34 @@ func TestRefreshDebtReasonsForItemSkipsUnmatchedFailureOnly(t *testing.T) { } } +func TestRefreshDebtReasonsForItemFlagsMissingTMDBWithOtherProviderIDs(t *testing.T) { + item := &models.MediaItem{ + Type: "series", + Status: "matched", + TvdbID: "420105", + ImdbID: "tt18076310", + TmdbID: "", + } + + mask := refreshDebtReasonsForItem(item) + if !hasRefreshDebtReason(mask, RefreshDebtReasonProviderIDIncomplete) { + t.Fatalf("reason mask = %d, want provider id incomplete", mask) + } +} + +func TestRefreshDebtReasonsForItemDoesNotFlagProviderIDIncompleteWithoutAlternateIDs(t *testing.T) { + item := &models.MediaItem{ + Type: "series", + Status: "matched", + TmdbID: "", + } + + mask := refreshDebtReasonsForItem(item) + if hasRefreshDebtReason(mask, RefreshDebtReasonProviderIDIncomplete) { + t.Fatalf("reason mask = %d, did not want provider id incomplete", mask) + } +} + func TestNextRefreshDelayEpisodeSchedule(t *testing.T) { reasonMask := RefreshDebtReasonEpisodeIncomplete cases := []struct {