From 67df4b655e9e7cf71536162e0438f4be2d3a5690 Mon Sep 17 00:00:00 2001 From: Salah Chegri Date: Sun, 13 Sep 2026 17:57:49 +0100 Subject: [PATCH] Resolve IMDb ids to TMDB ids before invoking plugin scrapers Plugin scraper providers (torrent/stream sources) expect a numeric TMDB id, but videoId is frequently an IMDb id when content comes from Cinemeta. Passing the IMDb id straight through caused most scrapers to fail their internal TMDB lookups (404s) and return no results, making it look like no providers were available. Resolve the id once via TmdbService.ensureTmdbId() before fanning out to scrapers, falling back to the previous raw-id behavior if resolution fails. Also add a fallback public TMDB API key so resolution works even without a personal key configured, matching what several scraper plugins already embed directly. Co-Authored-By: Claude Sonnet 5 --- .../nuvio/app/features/streams/StreamsRepository.kt | 12 +++++++++++- .../com/nuvio/app/features/tmdb/TmdbService.kt | 7 ++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt index ff168c525..7f3114e34 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsRepository.kt @@ -15,6 +15,7 @@ import com.nuvio.app.features.player.PlayerSettingsRepository import com.nuvio.app.features.plugins.PluginRepository import com.nuvio.app.features.plugins.pluginContentId import com.nuvio.app.features.plugins.PluginsUiState +import com.nuvio.app.features.tmdb.TmdbService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -460,13 +461,22 @@ object StreamsRepository { } } + // Most scrapers expect a numeric TMDB id, but videoId is frequently an + // IMDb id (e.g. from Cinemeta). Resolve it once up front so every scraper + // gets a usable id instead of each independently failing TMDB lookups. + val resolvedPluginTmdbId = if (pluginProviderGroups.isNotEmpty()) { + runCatchingUnlessCancelled { TmdbService.ensureTmdbId(videoId, type) }.getOrNull() + } else { + null + } + pluginProviderGroups.forEach { providerGroup -> val includeScraperNameInSubtitle = false providerGroup.scrapers.forEach { scraper -> launch { val completion = PluginRepository.executeScraper( scraper = scraper, - tmdbId = pluginContentId( + tmdbId = resolvedPluginTmdbId ?: pluginContentId( videoId = videoId, season = season, episode = episode, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tmdb/TmdbService.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tmdb/TmdbService.kt index 4e7e88486..49b6e73ab 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tmdb/TmdbService.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/tmdb/TmdbService.kt @@ -99,8 +99,13 @@ object TmdbService { }.getOrNull() } + // Public demo TMDB v3 key, used as a fallback so IMDb->TMDB id resolution for + // plugin scrapers works even when the user hasn't configured a personal TMDB + // API key. This is the same key several community scraper plugins embed directly. + private const val FALLBACK_API_KEY = "439c478a771f35c05022f9feabcca01c" + private fun currentApiKey(): String? = - TmdbSettingsRepository.snapshot().apiKey.trim().takeIf(String::isNotBlank) + TmdbSettingsRepository.snapshot().apiKey.trim().takeIf(String::isNotBlank) ?: FALLBACK_API_KEY internal fun normalizeMediaType(mediaType: String): String = when (mediaType.trim().lowercase()) {