mirror of
https://github.com/dirtydishes/dreamio.git
synced 2026-06-06 13:37:24 +00:00
accept stremio subtitle download urls
This commit is contained in:
parent
11ed364094
commit
11b9c6a12a
7 changed files with 540 additions and 1 deletions
|
|
@ -32,3 +32,4 @@
|
|||
{"id":"int-fe1c7364","kind":"field_change","created_at":"2026-05-25T16:04:54.482803Z","actor":"dirtydishes","issue_id":"dreamio-656","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"fixed"}}
|
||||
{"id":"int-f9deecdb","kind":"field_change","created_at":"2026-05-25T16:18:29.458162Z","actor":"dirtydishes","issue_id":"dreamio-urs","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Fixed by rejecting OpenSubtitles manifest.json_N identifiers as playable subtitle URLs, promoting file_id values to API download URLs, and adding parser coverage for the live log shape."}}
|
||||
{"id":"int-569ee372","kind":"field_change","created_at":"2026-05-25T16:22:50.024736Z","actor":"dirtydishes","issue_id":"dreamio-433","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Fixed by tightening OpenSubtitles subtitle URL filtering in the web bridge and Swift parser, plus adding regression coverage for logged artwork and addon endpoint false positives."}}
|
||||
{"id":"int-eca1f7f8","kind":"field_change","created_at":"2026-05-25T16:33:55.331041Z","actor":"dirtydishes","issue_id":"dreamio-9sp","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Accepted Stremio subtitle download URLs in the bridge, parser, resolver, and regression tests."}}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{"_type":"issue","id":"dreamio-8cz","title":"fix stremio external subtitle loading regression","description":"After adding late subtitle forwarding for native playback, Stremio external subtitle loading is failing. Investigate the injected bridge and native subtitle forwarding path, then adjust behavior so Stremio can still load external subtitles while native playback receives late candidates.","status":"closed","priority":0,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T11:05:42Z","created_by":"dirtydishes","updated_at":"2026-05-25T11:07:35Z","started_at":"2026-05-25T11:05:55Z","closed_at":"2026-05-25T11:07:35Z","close_reason":"Hardened subtitle bridge network observers so non-text Stremio subtitle loads are not touched, and made parser traversal deterministic for metadata preservation.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"dreamio-9sp","title":"Accept Stremio subtitle download URLs","description":"Runtime logs show Stremio external subtitle tracks using subs5.strem.io /en/download URLs. The subtitle bridge and Swift parser currently reject those URLs because they do not have a subtitle file extension and are not on an OpenSubtitles host, so native playback receives zero external subtitle candidates.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T16:32:04Z","created_by":"dirtydishes","updated_at":"2026-05-25T16:33:55Z","started_at":"2026-05-25T16:32:10Z","closed_at":"2026-05-25T16:33:55Z","close_reason":"Accepted Stremio subtitle download URLs in the bridge, parser, resolver, and regression tests.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"dreamio-433","title":"Filter false OpenSubtitles subtitle candidates","description":"Dreamio is treating addon artwork and OpenSubtitles addon endpoints as external subtitle candidates, which causes the native player UI to show only embedded subtitles. Tighten subtitle URL detection in the web bridge and Swift parser, and add regression coverage for the logged false positives.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T16:20:47Z","created_by":"dirtydishes","updated_at":"2026-05-25T16:22:50Z","started_at":"2026-05-25T16:20:50Z","closed_at":"2026-05-25T16:22:50Z","close_reason":"Fixed by tightening OpenSubtitles subtitle URL filtering in the web bridge and Swift parser, plus adding regression coverage for logged artwork and addon endpoint false positives.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"dreamio-urs","title":"Fix OpenSubtitles manifest-style subtitle URLs","description":"OpenSubtitles subtitle candidates discovered from Stremio are being resolved as manifest.json_N URLs, producing 404s and leaving only embedded subtitles visible. Preserve and resolve real subtitle URLs so external subtitle tracks can attach in the native player.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T16:16:52Z","created_by":"dirtydishes","updated_at":"2026-05-25T16:18:29Z","started_at":"2026-05-25T16:16:57Z","closed_at":"2026-05-25T16:18:29Z","close_reason":"Fixed by rejecting OpenSubtitles manifest.json_N identifiers as playable subtitle URLs, promoting file_id values to API download URLs, and adding parser coverage for the live log shape.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"dreamio-656","title":"Capture OpenSubtitles candidates from Stremio app-state messages","description":"OpenSubtitlesV3 appears loaded in Stremio before native playback launches, but Dreamio forwards zero external subtitle candidates. The likely failure is not native-player timing; it is that the injected WebKit bridge does not extract Stremio's loaded subtitle metadata/state into URL candidates before opening VLC.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T16:00:09Z","created_by":"dirtydishes","updated_at":"2026-05-25T16:04:54Z","started_at":"2026-05-25T16:00:18Z","closed_at":"2026-05-25T16:04:54Z","close_reason":"fixed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
|
|||
|
|
@ -169,12 +169,25 @@ final class DreamioWebViewController: UIViewController {
|
|||
}
|
||||
};
|
||||
|
||||
const isStremioSubtitleDownloadURL = (url) => {
|
||||
try {
|
||||
const parsed = new URL(url, window.location.href);
|
||||
const host = parsed.hostname.toLowerCase();
|
||||
const path = parsed.pathname.toLowerCase();
|
||||
return host === "strem.io" || host.endsWith(".strem.io")
|
||||
? /\/[a-z]{2,3}\/download(?:\/|$)/i.test(path) || /\/download(?:\/|$)/i.test(path)
|
||||
: false;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const isSubtitleURL = (url) => {
|
||||
if (!url || isOpenSubtitlesManifestID(url)) {
|
||||
return false;
|
||||
}
|
||||
return !isProbablyNonSubtitleAssetURL(url)
|
||||
&& (isDirectSubtitleFileURL(url) || isOpenSubtitlesDownloadURL(url));
|
||||
&& (isDirectSubtitleFileURL(url) || isOpenSubtitlesDownloadURL(url) || isStremioSubtitleDownloadURL(url));
|
||||
};
|
||||
|
||||
const findResolverURL = () => {
|
||||
|
|
|
|||
|
|
@ -256,6 +256,7 @@ enum SubtitleCandidateParser {
|
|||
}
|
||||
guard isDirectSubtitleFile(url)
|
||||
|| isOpenSubtitlesDownloadURL(url)
|
||||
|| isStremioSubtitleDownloadURL(url)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -282,6 +283,18 @@ enum SubtitleCandidateParser {
|
|||
|| path.range(of: #"(^|/)subtitles?(/|$)"#, options: .regularExpression) != nil
|
||||
}
|
||||
|
||||
private static func isStremioSubtitleDownloadURL(_ url: URL) -> Bool {
|
||||
guard let host = url.host?.lowercased(),
|
||||
host == "strem.io" || host.hasSuffix(".strem.io")
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
let path = url.path.lowercased()
|
||||
return path.range(of: #"^/[a-z]{2,3}/download(/|$)"#, options: .regularExpression) != nil
|
||||
|| path.range(of: #"(^|/)download(/|$)"#, options: .regularExpression) != nil
|
||||
}
|
||||
|
||||
private static func isOpenSubtitlesManifestIdentifier(_ url: URL) -> Bool {
|
||||
guard url.host?.localizedCaseInsensitiveContains("opensubtitles") == true else {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ final class SubtitleResolver: SubtitleResolving {
|
|||
let lowercased = url.absoluteString.lowercased()
|
||||
return ["srt", "vtt", "ass", "ssa", "sub"].contains(url.pathExtension.lowercased())
|
||||
|| [".srt?", ".vtt?", ".ass?", ".ssa?", ".sub?", ".srt&", ".vtt&", ".ass&", ".ssa&", ".sub&"].contains(where: lowercased.contains)
|
||||
|| isStremioSubtitleDownloadURL(url)
|
||||
}
|
||||
|
||||
private static func shouldResolve(_ url: URL) -> Bool {
|
||||
|
|
@ -138,6 +139,19 @@ final class SubtitleResolver: SubtitleResolving {
|
|||
return lowercased.contains("opensubtitles")
|
||||
|| lowercased.contains("/subtitle")
|
||||
|| lowercased.contains("subtitle")
|
||||
|| isStremioSubtitleDownloadURL(url)
|
||||
}
|
||||
|
||||
private static func isStremioSubtitleDownloadURL(_ url: URL) -> Bool {
|
||||
guard let host = url.host?.lowercased(),
|
||||
host == "strem.io" || host.hasSuffix(".strem.io")
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
let path = url.path.lowercased()
|
||||
return path.range(of: #"^/[a-z]{2,3}/download(/|$)"#, options: .regularExpression) != nil
|
||||
|| path.range(of: #"(^|/)download(/|$)"#, options: .regularExpression) != nil
|
||||
}
|
||||
|
||||
private static func logRejected(_ candidate: SubtitleCandidate, responseURL: URL?, data: Data) -> SubtitleCandidate? {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ struct StreamResolverTests {
|
|||
testOpenSubtitlesNestedAttributesFilesParsing()
|
||||
testOpenSubtitlesManifestIDsAreNotResolvedAsSubtitles()
|
||||
testOpenSubtitlesArtworkAndAddonEndpointsAreIgnored()
|
||||
testStremioSubtitleDownloadURLParsing()
|
||||
testOpenSubtitlesV3DownloadResponseResolution()
|
||||
testOpenSubtitlesNestedDownloadResponseResolution()
|
||||
await testSubtitleResolverDownloadJSONReturningLink()
|
||||
|
|
@ -240,6 +241,30 @@ struct StreamResolverTests {
|
|||
assertEqual(candidates[0].label, "English")
|
||||
}
|
||||
|
||||
private static func testStremioSubtitleDownloadURLParsing() {
|
||||
let payload: [String: Any] = [
|
||||
"subtitles": [
|
||||
[
|
||||
"label": "English",
|
||||
"lang": "eng",
|
||||
"url": "https://subs5.strem.io/en/download/subencoding-stremio-utf8/src-api/file/1952341941"
|
||||
],
|
||||
[
|
||||
"label": "Not a subtitle",
|
||||
"url": "https://www.strem.io/images/addons/opensubtitles-logo.png"
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
let candidates = SubtitleCandidateParser.candidates(in: payload)
|
||||
|
||||
assertEqual(candidates.count, 1)
|
||||
assertEqual(candidates[0].url.absoluteString, "https://subs5.strem.io/en/download/subencoding-stremio-utf8/src-api/file/1952341941")
|
||||
assertEqual(candidates[0].label, "English")
|
||||
assertEqual(candidates[0].language, "eng")
|
||||
assert(SubtitleResolver.isDirectSubtitleFile(candidates[0].url), "Expected Stremio subtitle downloads to be attachable without another resolver hop")
|
||||
}
|
||||
|
||||
private static func testOpenSubtitlesV3DownloadResponseResolution() {
|
||||
let payload = """
|
||||
{
|
||||
|
|
|
|||
472
docs/turns/2026-05-25-accept-stremio-subtitle-download-urls.html
Normal file
472
docs/turns/2026-05-25-accept-stremio-subtitle-download-urls.html
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue