From 312e0ae2ee8f11940b676f5d77fc233ae44c56c9 Mon Sep 17 00:00:00 2001 From: Thales Pereira <31625914+thcp@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:04:39 +0100 Subject: [PATCH 01/11] fix(mobile): guard against OOM crash on Load, fix stuck Preparing audio Two bugs reported in #234 (via discussion #216): 1. Mobile browsers crash (WebKit tab kill) when loading long tracks because createAudioEngine decodes all stems into AudioBuffers in parallel. Added an estimateDecodedBytes check before creating the engine: tracks whose decoded PCM would exceed 200 MB (approx 4.5 min x 4 stems) now surface a clear error instead of silently crashing the tab. 2. After a crash-induced reload, loadLibrary auto-selected state.tracks[0] without calling openTrack(), leaving the player stuck on "Preparing audio" indefinitely. Removed the auto-select: the library is visible on load and the user can pick a track explicitly. Closes #234 --- static/mobile/app.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/static/mobile/app.js b/static/mobile/app.js index 3f22521f..6b7f0b75 100644 --- a/static/mobile/app.js +++ b/static/mobile/app.js @@ -4,7 +4,7 @@ // (GET /api/jobs, /api/jobs/{id}, the Web Audio engine, mixdown export). // Extract is still mock pending the SSE/upload wiring (next step). import { fetchJobs, jobToCard } from "../js/shared/jobs.js"; -import { createAudioEngine } from "../js/audioEngine.js"; +import { createAudioEngine, estimateDecodedBytes } from "../js/audioEngine.js"; // Per-stem label + color, keyed by the backend stem name. Unknown names fall // back to a rotating palette so non-standard models still render sensibly. @@ -259,6 +259,16 @@ async function openTrack(card, { autoplay = false } = {}) { return; } + // Mobile browsers crash (OOM tab kill) when decoding all stems into AudioBuffers + // for long tracks. 200 MB covers ~4.5 min x 4 stems at 44.1 kHz / Float32. + const MOBILE_DECODE_LIMIT = 200e6; + const estimatedBytes = estimateDecodedBytes(detail.duration || 0, laneList.length); + if (estimatedBytes > MOBILE_DECODE_LIMIT) { + state.current.error = "Track too long to load on mobile. Try a shorter track (under ~5 minutes)."; + render(); + return; + } + engine = createAudioEngine(laneList.map((l) => ({ name: l.name, url: l.url })), { onTime: onEngineTime, onEnded: () => { state.playing = false; render(); }, @@ -890,7 +900,6 @@ async function loadLibrary() { const jobs = await fetchJobs(); state.tracks = jobs.map(jobToCard).sort((a, b) => b.createdAt - a.createdAt); state.libState = state.tracks.length ? "ready" : "empty"; - if (!state.current && state.tracks.length) state.current = state.tracks[0]; } catch (e) { console.warn("[mobile] failed to load library:", e); state.libState = "error"; From 03a1d8488bfe002e0cd1ec382b43900ed2130589 Mon Sep 17 00:00:00 2001 From: Thales Pereira <31625914+thcp@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:41:32 +0100 Subject: [PATCH 02/11] fix(mobile): replace OOM hard error with streaming engine fallback Instead of blocking long tracks with an error, tracks that would exceed 200 MB of decoded PCM (approx 4.5 min x 4 stems) now fall back to a streaming engine backed by