2.8 KiB
2.8 KiB
id, title, created, depends_on, est_files
| id | title | created | depends_on | est_files | |
|---|---|---|---|---|---|
| 004-coalesce-stream-resolves-a92d40 | Coalesce concurrent resolveStreams calls for the same video | 2026-09-29 |
|
1 |
004 — Coalesce concurrent resolveStreams calls
Objective
resolveStreams(videoId) checks streamCache but has no in-flight map, so two
requests for the same id that arrive before the first finishes (a warm-up + the
real play, two devices, the media cache's getInfo + /api/streams) each spawn a
~6 s yt-dlp -J. After this plan, concurrent callers share ONE promise; a failure
is not cached (the next call retries).
Context the executor must NOT rediscover
server/server.js:457-494:
async function resolveStreams(videoId) {
const now = Date.now();
const cached = streamCache.get(videoId);
if (cached && now < cached.expiresAt) return cached;
const out = await runYtdlpResilient(['-J', '--no-warnings', `https://www.youtube.com/watch?v=${videoId}`]);
const info = JSON.parse(out);
...
streamCache.set(videoId, entry);
return entry;
}
streamCache and STREAM_CACHE_MAX are declared just above it.
Steps
server/server.js— rename the existing functionresolveStreamstoresolveStreamsUncached(definition only; body unchanged).- Directly after that function add:
All existing callers keep calling
// One yt-dlp -J per video at a time: concurrent callers (warm-up + play, // two devices, the media cache's getInfo) share the in-flight promise. const inflightStreams = new Map(); // videoId -> Promise<entry> function resolveStreams(videoId) { const cached = streamCache.get(videoId); if (cached && Date.now() < cached.expiresAt) return Promise.resolve(cached); let p = inflightStreams.get(videoId); if (!p) { p = resolveStreamsUncached(videoId).finally(() => inflightStreams.delete(videoId)); inflightStreams.set(videoId, p); } return p; }resolveStreams(it still returns a Promise). - Create
server/streams-inflight.test.js? — NO. Keep it in-file; instead verify with the script below.
Out of scope / do NOT touch
- Cache TTL logic,
runYtdlpResilient, format filtering.
Verification
cd /home/user/ytplayer/server && grep -n "function resolveStreams\|function resolveStreamsUncached\|inflightStreams" server.js
bun build server.js --target=bun --outdir=/tmp/ytp-check >/dev/null && echo SERVER_OK
bun run test 2>&1 | grep -E "^ *[0-9]+ (pass|fail)"
Expected: both functions + map present, SERVER_OK, all server test files 0 fail.
Report format (executor: follow exactly)
Output ONLY the following, no other prose:
git diff(unified) of all changes.- Raw output of the Verification commands.
Findings:— max 10 lines.
Do not commit. Do not push. Do not touch files outside the Steps.