From d11275e8af01888ae5aad0cfa4d19d3c0660511d Mon Sep 17 00:00:00 2001 From: Arvin Date: Sun, 5 Apr 2026 14:57:05 +0200 Subject: [PATCH] fix: recognize custom subtitle addons like Wizdom and Ktuvit (#80) User-added Stremio addons that declared only the `subtitles` resource in their manifest (Wizdom, Ktuvit, and other regional subtitle providers) were installed successfully but were functionally inert. The root cause was two-fold: 1. `StreamRepository.addCustomAddon` hardcoded `type = AddonType.CUSTOM` for every user-added addon regardless of what resources its manifest declared. So a pure-subtitle addon ended up in the same bucket as a stream addon. 2. `fetchSubtitlesForSelectedStream` then filtered strictly on `type == AddonType.SUBTITLE`, which only matched the built-in OpenSubtitles addon. Pure-subtitle addons stored as CUSTOM were never queried for subtitles. The result was that Israeli users who added Wizdom or Ktuvit saw them listed and enabled in Settings \u2192 Addons but never saw any Hebrew subtitles appear during playback \u2014 effectively making ARVIO unusable for them as reported in issue #80. Changes: - `addCustomAddon` now inspects the parsed manifest's resources. If the addon declares `subtitles` but not `stream`, it gets `AddonType.SUBTITLE`; otherwise it stays CUSTOM (preserving the existing behavior for stream addons and hybrid addons). - `fetchSubtitlesForSelectedStream` now also includes CUSTOM-typed addons whose manifest declares a `subtitles` resource. This covers two cases: (a) addons installed before this fix that are still classified as CUSTOM in DataStore; (b) hybrid addons that legitimately provide both streams and subtitles and should be queried for both. - The stream-side filter in `getStreamAddons` already requires CUSTOM addons to declare `stream` resource, so newly-classified SUBTITLE addons are correctly excluded from stream fetches and don't pollute the stream picker. Existing users with Wizdom/Ktuvit already installed will benefit immediately from change (2) without needing to reinstall. New installations will benefit from both changes. Closes #80 --- .../tv/data/repository/StreamRepository.kt | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/arflix/tv/data/repository/StreamRepository.kt b/app/src/main/kotlin/com/arflix/tv/data/repository/StreamRepository.kt index e82ee68dc..e325ffd00 100644 --- a/app/src/main/kotlin/com/arflix/tv/data/repository/StreamRepository.kt +++ b/app/src/main/kotlin/com/arflix/tv/data/repository/StreamRepository.kt @@ -249,6 +249,25 @@ class StreamRepository @Inject constructor( val resolvedName = customName?.trim()?.takeIf { it.isNotBlank() } ?: manifest.name val addonId = buildAddonInstanceId(manifest.id, normalizedUrl) + // Classify the addon based on the resources its manifest declares. + // - If it declares `subtitles` but NOT `stream`, it's a pure subtitle addon + // (e.g. Wizdom, Ktuvit) and gets AddonType.SUBTITLE so the subtitle fetcher + // picks it up and the stream resolver correctly ignores it. + // - If it declares `stream` (with or without subtitles), keep it as CUSTOM so + // the stream resolver queries it. The subtitle fetcher has been updated + // separately to also include CUSTOM addons whose manifest declares a + // subtitles resource, so hybrid addons still get queried for both. + // - Everything else stays CUSTOM, matching the previous default. + // Fixes issue #80 where Wizdom/Ktuvit were installed but never queried because + // every user-added addon was hardcoded to CUSTOM regardless of its manifest. + val resourceNames = addonManifest.resources.map { it.name }.toSet() + val hasSubtitles = "subtitles" in resourceNames + val hasStream = "stream" in resourceNames + val addonType = when { + hasSubtitles && !hasStream -> AddonType.SUBTITLE + else -> AddonType.CUSTOM + } + val newAddon = Addon( id = addonId, name = resolvedName, @@ -256,7 +275,7 @@ class StreamRepository @Inject constructor( description = manifest.description ?: "", isInstalled = true, isEnabled = true, - type = AddonType.CUSTOM, + type = addonType, url = normalizedUrl, logo = manifest.logo, manifest = addonManifest, @@ -1336,7 +1355,26 @@ class StreamRepository @Inject constructor( stream: StreamSource? ): List = withContext(Dispatchers.IO) { val allAddons = installedAddons.first() - val subtitleAddons = allAddons.filter { it.isInstalled && it.isEnabled && it.type == AddonType.SUBTITLE } + // Include: + // - Addons classified as AddonType.SUBTITLE (OpenSubtitles and, going forward, + // any user-added pure-subtitle addon like Wizdom/Ktuvit now that addCustomAddon + // classifies them correctly). + // - Addons classified as CUSTOM whose manifest declares a `subtitles` resource. + // This covers two cases: (a) addons installed before the classification fix + // landed, which are still stored as CUSTOM; (b) hybrid addons that provide + // both streams and subtitles. Both should be queried for subtitles. + // Fixes issue #80. + val subtitleAddons = allAddons.filter { addon -> + if (!addon.isInstalled || !addon.isEnabled) return@filter false + if (addon.type == AddonType.SUBTITLE) return@filter true + if (addon.type == AddonType.CUSTOM) { + val declaresSubtitles = addon.manifest?.resources?.any { res -> + res.name.equals("subtitles", ignoreCase = true) + } == true + return@filter declaresSubtitles + } + false + } val videoHash = stream?.behaviorHints?.videoHash?.trim().takeUnless { it.isNullOrBlank() } val videoSize = stream?.behaviorHints?.videoSize?.takeIf { it != null && it > 0L }