diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-10-21 23:47:13 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-10-21 23:47:13 +0000 |
| commit | 4dd59a40c8a8fed3008082da45e29c1c188b7fe8 (patch) | |
| tree | 6beb517fe10c731013bbdd42c4c88c81cc4fe8a7 /app/src/test/java/org | |
| parent | 9210bf463a8818595f7ca42486a787d4f1941d55 (diff) | |
Play the YouTube embed with a referrer instead of in an iframe
Peter, on the previous build: "the inline player now plays the audio but
the video is a black screen." So Error 153 is gone and the stream is
decoding -- what is left is a display problem.
The iframe was not needed in the first place. A `Referer` header alone
satisfies the embed, which means the player can be the WebView's own
ordinary top-level document again. That drops two constructs the last
build added: a `loadDataWithBaseURL` page, whose origin is synthetic,
and a cross-origin subframe inside it. Both have a history of rendering
video oddly in WebView, and the black screen appeared with them.
Measured on an Android user agent -- no referrer: Error 153; `&origin=`
in the query: Error 153; inside an iframe: plays; with a `Referer`
header: plays. The referrer is a constant on a reserved `.invalid`
domain rather than the article's URL, so YouTube learns which app asked
and nothing about what is being read -- the same reason the nocookie
domain is used.
Confirmed in the app's own WebView: the embed loads with no error,
854x480, readyState 4, playing.
NOT confirmed: that a picture now reaches the screen. This emulator
cannot render video in a WebView at all -- it hard-crashed the AVD
twice, including on a plain <video> in an ordinary non-dialog WebView --
so the one thing that matters here can only be checked on real
hardware.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'app/src/test/java/org')
| -rw-r--r-- | app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt | 45 |
1 files changed, 18 insertions, 27 deletions
diff --git a/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt b/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt index 3afba16..af9884d 100644 --- a/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt +++ b/app/src/test/java/org/terst/feedreader/ui/YouTubeLinksTest.kt @@ -90,46 +90,37 @@ class YouTubeLinksTest { assertNull(youTubeTargetForLongPress("not a url at all")) } - // --- the embed page ------------------------------------------------------------------ + // --- the embed ---------------------------------------------------------------------- @Test - fun `the embed page puts the player in an iframe, which is the whole point`() { - val html = youTubeEmbedPageHtml("dQw4w9WgXcQ") + fun `the embed url is the privacy-preserving nocookie one, playing inline`() { + val url = youTubeEmbedUrl("dQw4w9WgXcQ") - assertTrue("loading the embed top-level is what YouTube rejects: $html", html.contains("<iframe")) - assertTrue(html.contains("https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ")) + assertTrue(url.startsWith("https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ")) + assertTrue(url.contains("autoplay=1")) + assertTrue(url.contains("playsinline=1")) } @Test - fun `the embed page keeps autoplay and inline playback`() { - val html = youTubeEmbedPageHtml("dQw4w9WgXcQ") - - assertTrue(html.contains("autoplay=1")) - assertTrue(html.contains("playsinline=1")) - assertTrue("without this the iframe is not allowed to autoplay", html.contains("allow=\"autoplay")) + fun `the embed is sent a referrer, which is the whole reason it plays`() { + assertTrue(YOUTUBE_EMBED_REFERRER.startsWith("https://")) } @Test - fun `the base URL is a real https origin, since that is what the embed requires`() { - assertTrue(YOUTUBE_EMBED_BASE_URL.startsWith("https://")) - } - - @Test - fun `the base URL is not YouTube's own origin`() { - assertFalse( - "the embed has to be a guest on somebody else's page, not its own", - YOUTUBE_EMBED_BASE_URL.contains("youtube") - ) + fun `the referrer says nothing about what is being read`() { + // A constant, so YouTube learns the app and nothing else. The article's own URL would + // work just as well and would tell YouTube what he reads, which is the thing the + // nocookie domain is already chosen to avoid. + assertFalse(YOUTUBE_EMBED_REFERRER.contains("?")) + assertTrue("a reserved TLD can never become somebody's real site", YOUTUBE_EMBED_REFERRER.contains(".invalid")) } @Test - fun `a video id that could break out of the attribute is scrubbed`() { - val html = youTubeEmbedPageHtml("""a"><script>alert(1)</script>""") + fun `a video id that could break out of the url is scrubbed`() { + val url = youTubeEmbedUrl("""a"><script>alert(1)</script>""") - assertFalse("markup from the id reached the page: $html", html.contains("<script")) - assertFalse(html.contains("alert(1)")) - // What is left is only ever id characters, so the src attribute still closes where it should. - assertTrue(html.contains("embed/ascriptalert1script?")) + assertFalse("markup from the id reached the url: $url", url.contains("<")) + assertTrue(url.startsWith("https://www.youtube-nocookie.com/embed/ascriptalert1script?")) } @Test |
