fix(playback): stop a moment without signal from pausing the phone

A phone changing cell or locking its screen drops the push stream for a
few seconds while its audio keeps playing. The server released session
ownership the instant that stream closed, and the phone read its own
reconnect snapshot as another device taking over: it paused, and rewound
to whatever position it had last reported. Ownership now survives a
closed stream. A device that is really gone still loses the session, via
pagehide and via the sweep that frees an owner whose heartbeat stopped.

The client no longer treats an unowned session as an instruction to
stop. With audio loaded it claims the session back instead.

Two ways a phone could go quiet until the page was reloaded are also
gone: registration is retried rather than attempted once, and the push
stream reopens after an error status, which EventSource treats as final.
It also checks itself when the network or the tab comes back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KENqSChfyqWnor6ud2WWH6
This commit is contained in:
kami
2026-08-10 13:59:29 +04:00
parent 5a73a6a6f3
commit 60085c1d72
4 changed files with 112 additions and 19 deletions
+7 -3
View File
@@ -200,9 +200,13 @@ export default async function playbackRoutes(
request.raw.on('close', () => {
clearInterval(heartbeat);
unsubscribe();
// A closed tab must not keep the session hostage: hand ownership back so
// any other device can pick the same track up where this one left it.
playbackSync.releaseIfOwner(userId, deviceId).catch(() => {});
// Ownership deliberately survives a closed stream. A phone changing cell,
// locking its screen or dozing drops this connection for a few seconds
// while its audio keeps playing; releasing here published an unowned
// session, which the phone then read as "something else took over" and
// paused itself. A device that is really gone loses the session two other
// ways: `pagehide` releases it outright, and the stale-device sweep frees
// an owner whose heartbeat has been silent past DEVICE_STALE_MS.
});
});
}