From 5be7d4c2761999db89d2af6f338020efe7660a62 Mon Sep 17 00:00:00 2001 From: Kayos Date: Sun, 24 May 2026 09:33:34 -0700 Subject: [PATCH] =?UTF-8?q?v0.1.0-W2=20(vc=3D11):=20fix=20playback=20?= =?UTF-8?q?=E2=80=94=20TV+Ios=20YT=20clients=20+=20visible=20play=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Black-screen-on-play bug: rustypipe's default player() uses YT's Web client, which serves stream URLs that are session/UA-locked. ExoPlayer fetching with a different UA gets a silent 403 from googlevideo and renders a black surface. Fix: pin stream_info() to player_from_clients(id, [Tv, Ios]) — the TVHTML5 + iOS Innertube clients serve direct-play URLs that work in any HTTP player. Same trick NewPipe uses. No Apple/iOS code involved — it's just the API client identifier rustypipe sends to YT. Also added a Player.Listener in PlayerScreen that Toasts any PlaybackException (codeName + message) so future stream failures don't look like silent black screens. Logs to logcat 'StrawPlayer' too. --- buildSrc/src/main/kotlin/ProjectConfig.kt | 4 ++-- rust/strawcore/src/stream.rs | 12 ++++++++-- .../straw/feature/player/PlayerScreen.kt | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/ProjectConfig.kt b/buildSrc/src/main/kotlin/ProjectConfig.kt index a63073c18..5f3a39428 100644 --- a/buildSrc/src/main/kotlin/ProjectConfig.kt +++ b/buildSrc/src/main/kotlin/ProjectConfig.kt @@ -15,6 +15,6 @@ const val NEWPIPE_APPLICATION_ID_OLD = "org.schabi.newpipe" const val NEWPIPE_APPLICATION_ID_NEW = "net.newpipe.app" // Sulkta fork — Straw -const val STRAW_VERSION_CODE = 10 -const val STRAW_VERSION_NAME = "0.1.0-W" +const val STRAW_VERSION_CODE = 11 +const val STRAW_VERSION_NAME = "0.1.0-W2" const val STRAW_APPLICATION_ID = "com.sulkta.straw" diff --git a/rust/strawcore/src/stream.rs b/rust/strawcore/src/stream.rs index d388c5235..ea35ac76b 100644 --- a/rust/strawcore/src/stream.rs +++ b/rust/strawcore/src/stream.rs @@ -17,7 +17,7 @@ use crate::error::StrawcoreError; use crate::search::SearchItem; -use rustypipe::client::RustyPipe; +use rustypipe::client::{ClientType, RustyPipe}; #[derive(Debug, Clone, uniffi::Record)] pub struct StreamInfo { @@ -129,7 +129,15 @@ pub async fn stream_info(url: String) -> Result { log::info!("strawcore::stream_info id={}", id); let rp = RustyPipe::new(); - let player = rp.query().player(&id).await?; + // rustypipe's default `player()` uses the Web client first. Those URLs + // come back signed against the Web fetch's session/UA — ExoPlayer can't + // replay them (404/403/black screen). Force the TV embedded + iOS + // clients, both of which return ungated direct-play URLs the way + // NewPipe's resolver does. + let player = rp + .query() + .player_from_clients(&id, &[ClientType::Tv, ClientType::Ios]) + .await?; let details = &player.details; // Progressive (combined audio+video) goes through video_streams; the diff --git a/strawApp/src/main/kotlin/com/sulkta/straw/feature/player/PlayerScreen.kt b/strawApp/src/main/kotlin/com/sulkta/straw/feature/player/PlayerScreen.kt index dfb0518e0..5279e8fdc 100644 --- a/strawApp/src/main/kotlin/com/sulkta/straw/feature/player/PlayerScreen.kt +++ b/strawApp/src/main/kotlin/com/sulkta/straw/feature/player/PlayerScreen.kt @@ -121,6 +121,28 @@ fun PlayerScreen( } } + // Surface playback errors so a 403/404 from googlevideo doesn't show + // as a silent black screen. Captures everything ExoPlayer's renderer + // pipeline raises. + DisposableEffect(exoPlayer) { + val listener = object : Player.Listener { + override fun onPlayerError(error: androidx.media3.common.PlaybackException) { + val msg = buildString { + append("play err ") + append(error.errorCodeName) + append(": ") + append(error.message ?: error.cause?.message ?: "?") + } + com.sulkta.straw.util.strawLogW("StrawPlayer") { "$msg" } + runCatching { + Toast.makeText(context, msg.take(160), Toast.LENGTH_LONG).show() + } + } + } + exoPlayer.addListener(listener) + onDispose { exoPlayer.removeListener(listener) } + } + // PiP setup: on Android 12+ tell the OS this activity can auto-enter // PiP, so when the user presses Home or swipes away the video shrinks // into a floating window instead of pausing/exiting. Aspect ratio is