Dreamio now rejects OpenSubtitles addon manifest identifiers such as manifest.json_14 as playable subtitle URLs. When the same payload includes a real OpenSubtitles file_id, Dreamio promotes that ID to the API download endpoint instead.
Live debug logs showed twenty OpenSubtitles candidates reaching the native player, but every candidate resolved as https://opensubtitles-v3.strem.io/manifest.json_N and returned HTTP 404. That left VLC with only the embedded MKV subtitle track visible in the UI.
114 unmodified lines11511611711811912079 unmodified lines200201202203204205206207208209210211212213214114 unmodified lines}};+const findResolverURL = () => {const links = Array.from(document.querySelectorAll("a[href], [data-href], [data-url]"));const match = links79 unmodified linesentry.fileURL);let url = absoluteURL(rawURL);if (!url && entry && entry.file_id) {url = `https://api.opensubtitles.com/api/v1/download/${encodeURIComponent(String(entry.file_id))}`;}subtitleURLPattern.lastIndex = 0;if (!url || (!subtitleURLPattern.test(url) && !/api\.opensubtitles\.com\/api\/v1\/download/i.test(url))) {subtitleURLPattern.lastIndex = 0;return;}subtitleURLPattern.lastIndex = 0;if (subtitleCandidates.some((candidate) => candidate.url === url)) {return;}114 unmodified lines11511611711811912012112212312412512612712812913013113213313413513613713813914079 unmodified lines220221222223224225226227228229230231114 unmodified lines}};+const isOpenSubtitlesManifestID = (url) => {try {const parsed = new URL(url, window.location.href);return /opensubtitles/i.test(parsed.hostname)&& /\/manifest\.json(?:_\d+)?$/i.test(parsed.pathname);} catch (_) {return false;}};+const isSubtitleURL = (url) => {if (!url || isOpenSubtitlesManifestID(url)) {return false;}subtitleURLPattern.lastIndex = 0;const matches = subtitleURLPattern.test(url) || /api\.opensubtitles\.com\/api\/v1\/download/i.test(url);subtitleURLPattern.lastIndex = 0;return matches;};+const findResolverURL = () => {const links = Array.from(document.querySelectorAll("a[href], [data-href], [data-url]"));const match = links79 unmodified linesentry.fileURL);let url = absoluteURL(rawURL);if ((!url || isOpenSubtitlesManifestID(url)) && entry && entry.file_id) {url = `https://api.opensubtitles.com/api/v1/download/${encodeURIComponent(String(entry.file_id))}`;}if (!isSubtitleURL(url)) {return;}if (subtitleCandidates.some((candidate) => candidate.url === url)) {return;}
244 unmodified lines2452462472482492505 unmodified lines256257258259260261244 unmodified lines}+let lowercased = url.absoluteString.lowercased()guard supportedExtensions.contains(url.pathExtension.lowercased())|| supportedExtensions.contains(where: { lowercased.contains(".\($0)?") || lowercased.contains(".\($0)&") })|| lowercased.contains("subtitle")5 unmodified linesreturn url}+private static func openSubtitlesDownloadURL(from value: Any?) -> URL? {let id: String?if let string = value as? String, !string.isEmpty {244 unmodified lines2452462472482492502512522535 unmodified lines259260261262263264265266267268269270271272244 unmodified lines}+let lowercased = url.absoluteString.lowercased()if isOpenSubtitlesManifestIdentifier(url) {return nil}guard supportedExtensions.contains(url.pathExtension.lowercased())|| supportedExtensions.contains(where: { lowercased.contains(".\($0)?") || lowercased.contains(".\($0)&") })|| lowercased.contains("subtitle")5 unmodified linesreturn url}+private static func isOpenSubtitlesManifestIdentifier(_ url: URL) -> Bool {guard url.host?.localizedCaseInsensitiveContains("opensubtitles") == true else {return false}let path = url.path.lowercased()return path == "/manifest.json" || path.range(of: #"/manifest\.json_\d+$"#, options: .regularExpression) != nil}+private static func openSubtitlesDownloadURL(from value: Any?) -> URL? {let id: String?if let string = value as? String, !string.isEmpty {
10 unmodified lines111213141516172 unmodified lines18919019119219319410 unmodified linestestSubtitleCandidateParsing()testOpenSubtitlesV3CandidateParsing()testOpenSubtitlesNestedAttributesFilesParsing()testOpenSubtitlesV3DownloadResponseResolution()testOpenSubtitlesNestedDownloadResponseResolution()await testSubtitleResolverDownloadJSONReturningLink()172 unmodified linesassertEqual(candidates[1].language, "eng")}+private static func testOpenSubtitlesV3DownloadResponseResolution() {let payload = """{10 unmodified lines11121314151617172 unmodified lines19019119219319419519619719819920020120220320420520620720820921021121221321421521621721810 unmodified linestestSubtitleCandidateParsing()testOpenSubtitlesV3CandidateParsing()testOpenSubtitlesNestedAttributesFilesParsing()testOpenSubtitlesManifestIDsAreNotResolvedAsSubtitles()testOpenSubtitlesV3DownloadResponseResolution()testOpenSubtitlesNestedDownloadResponseResolution()await testSubtitleResolverDownloadJSONReturningLink()172 unmodified linesassertEqual(candidates[1].language, "eng")}+private static func testOpenSubtitlesManifestIDsAreNotResolvedAsSubtitles() {let payload: [String: Any] = ["subtitles": [["url": "https://opensubtitles-v3.strem.io/manifest.json_14","file_id": 98765,"lang": "eng"],["url": "https://opensubtitles-v3.strem.io/manifest.json_15","lang": "spa"],"https://opensubtitles-v3.strem.io/manifest.json_16"]]+let candidates = SubtitleCandidateParser.candidates(in: payload)+assertEqual(candidates.count, 1)assertEqual(candidates[0].url.absoluteString, "https://api.opensubtitles.com/api/v1/download/98765")assertEqual(candidates[0].language, "eng")}+private static func testOpenSubtitlesV3DownloadResponseResolution() {let payload = """{
Related Beads issue: dreamio-urs.