Repository navigation
SideloadedSubtitle: Resolve playlist URIs without decoding existing percent-escapes - #134
Open
devin-ai-integration[bot] wants to merge 7 commits into
Open
devin-ai-integration[bot] wants to merge 7 commits into
devin-ai-integration[bot] wants to merge 7 commits into
Conversation
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
getFullURL built absolute URLs for relative URIs with appendingPathComponent. That method percent-encodes '?' into '%3F', so a variant URI such as 'video.m3u8?hdntl=...' became part of the path and the server returned HTTP 400. Resolve the URI against the manifest URL with URL(string:relativeTo:) instead. Fall back to the previous behavior when that fails.
Cover relative URIs with a query string in the same directory, in a subdirectory and in a parent directory, a relative URI without a query, and an absolute URI. A relative URI without its own query does not get the playlist's query, as RFC 3986 section 5.2.2 specifies.
Co-Authored-By: raffi.manissalian <raffi.manissalian@dolby.com>
Use URL(string:relativeTo:) on the trimmed URI as written instead of removing and re-adding percent-encoding, so escaped characters such as %3F, %26, %2B, %3D and %2F stay as data. Blank lines still return nil. Add regression tests for encoded filenames, escaped query values, redirects and playlist URI rewriting. Co-Authored-By: raffi.manissalian <raffi.manissalian@dolby.com>
Co-Authored-By: raffi.manissalian <raffi.manissalian@dolby.com>
devin-ai-integration
Bot
force-pushed
the
improvement/sideloaded-subtitles-preserve-uri-encoding
branch
from
November 6, 2026 15:54
0931ed5 to
a51dec6
Compare
…'t be parsed Co-Authored-By: raffi.manissalian <raffi.manissalian@dolby.com>
Co-Authored-By: raffi.manissalian <raffi.manissalian@dolby.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on #132 (contains its 3 commits). #132 fixes relative URIs with a query string (
video.m3u8?hdntl=...no longer becomesvideo.m3u8%3Fhdntl=...), butPlaylistParser.getFullURL(from:)still decodes all percent-escapes and then re-encodes the string. That step can't tell an escaped character that is data apart from a delimiter, so some valid URIs are still rewritten wrongly.getFullURL(from:)now uses the URI as written:The decode/re-encode step and the
encodingInvalidCharacterscheck were added in #37 only becauseappendingPathComponentescapes its input again (%20->%2520). This change removesappendingPathComponent, so those workarounds are no longer needed. TheisEmptyguard is required:VariantPlaylistParserpasses blank lines togetFullURL, andURL(string: "", relativeTo:)would resolve them to the manifest URL itself..../vod/movie/index.m3u8)video%3Falt.m3u8(escaped?in the filename).../video%3Falt.m3u8.../video?alt.m3u8.../video%3Falt.m3u8seg1.ts?exp=2&sig=ab%2Fcd%2B%3D%3D?becomes%3F, signature decodedab/cd+==video.m3u8?token=a%26b%2Bc%3Dd?becomes%3F, value decodeda&b+c=dproxy/https%3A%2F%2Forigin.com%2Fvideo.m3u8%3Ftoken%3Dabc?becomes a queryMK-...-227%20Soy%20una%20tazaUHD.m3u8(#37)%20kept%20kept%20keptInvalid characters (raw spaces): from iOS 17,
URL(string:)escapes invalid characters itself. Before iOS 17 it returnsnil, soVariantPlaylistParserwould drop a segment line such asseg one.tsfrom the rewritten playlist. When the first parse fails,getFullURLescapes only the invalid characters (existing%XXescapes and URL delimiters are kept) and tries once more. Valid URIs never reach this fallback. It checks the parse result instead of the OS version, because the iOS 17 parsing also depends on the SDK the app is linked against.Tests
20 new tests in
PlaylistParserTests, alongside the 5 from #132. All use hard-coded expected strings:getFullURL:%20,%25and%2520are kept as written; escaped%3F,%26,%2B,%3Dand%2Fare kept; already-encoded absolute URLs are not changed; root-relative and scheme-relative URIs;../does not inherit the manifest's query; surrounding whitespace is trimmed; a raw space is escaped to%20; empty and whitespace-only input returnsnil.URI=attributes;EXT-X-KEYandEXT-X-MAPwithEXT-X-BYTERANGEbetweenEXTINFand the segment;THEOplayerConnectorSideloadedSubtitleTests: 36/36 pass on the iOS 16.4 and iOS 27.0 simulators (run locally). Before the two raw-space tests were added, the #132 code passed 29/34 of the same tests andmainpassed 18/34.Tests in
PlaylistParserTestsare grouped withMARKs by the PR whose behavior they cover (#12, #17, #20, #37, #132, #134).Added a CHANGELOG entry under Unreleased → Fixed → SideloadedSubtitle.
Link to Devin session: https://dolby.devinenterprise.com/sessions/e338c69f5ccb4f599ef26ccbc7bd4a23
Open in Devin Desktop: https://dolby.devinenterprise.com/desktop/session/e338c69f5ccb4f599ef26ccbc7bd4a23?variant=devin