5.5 KiB
5.5 KiB
id, title, created, depends_on, est_files
| id | title | created | depends_on | est_files |
|---|---|---|---|---|
| 001-perf-timing-marks-105acc | Add startup, search and play timing marks plus yt-dlp duration logs | 2026-09-29 | 2 |
001 — Add startup, search and play timing marks plus yt-dlp duration logs
Objective
Every later speed plan needs a before/after number. After this plan:
- The browser records
performance.measureentriesytp:boot,ytp:search,ytp:tap-to-play, andwindow.__ytpPerf()returns their latest values in ms. - The server logs one line per yt-dlp call:
[ytdlp] <kind> <ms>ms ok|fail.
Measured baseline (prod, 2026-09-29): search 4.1–5.0 s, first play /api/streams 7.5 s,
second play 1.2 s, /api/version 1.25 s.
Context the executor must NOT rediscover
server/server.js:143-165 — the only place yt-dlp is spawned:
function runYtdlp(args, { signal } = {}) {
return new Promise((resolve, reject) => {
const child = spawn(YTDLP, args, { stdio: ['ignore', 'pipe', 'pipe'] });
...
child.on('error', (e) => reject(new Error('yt-dlp not found: ' + e.message)));
child.on('close', (code) => {
if (code !== 0) reject(new Error(err.trim() || 'yt-dlp exited with code ' + code));
else resolve(out);
});
});
}
frontend/app.js:
async function boot()starts at ~line 9749 (wirePlayerEvents();is its first line). The firstrender()call inside boot happens after thehasShareParamblock.async function runSearchQuery(q, { instant = null } = {})at ~line 8583; the success path ends withRecentSearches.cacheResults(q, results);.Player.loadVideo(videoObj, …)at ~line 1650; first statement isif (!this._handoff) Transition.cancel();.- The master element
playinglistener at ~line 2287:el.addEventListener('playing', () => { if (!masterIs(el)) return; showSpinner(false);
Steps
server/server.js— inrunYtdlp, directly after theconst child = spawn(...)line add:and replace theconst t0 = Date.now(); const kind = String(args.find((a) => /^ytsearch|^https?:/.test(String(a))) || args[0] || '') .replace(/^ytsearch\d*:.*/, 'search').replace(/^https?:\/\/[^/]+\/watch.*/, 'video').slice(0, 40);child.on('close', …)handler body with:child.on('close', (code) => { console.log(`[ytdlp] ${kind} ${Date.now() - t0}ms ${code === 0 ? 'ok' : 'fail'}`); if (code !== 0) reject(new Error(err.trim() || 'yt-dlp exited with code ' + code)); else resolve(out); });frontend/app.js— near the top of the file, directly after the lineconst APP_VERSION = '1.0.0';(~line 24), add:// Timing marks for the speed work (plans/). performance.measure entries are // visible in DevTools → Performance; __ytpPerf() prints the latest ones. function perfMark(name) { try { performance.mark(name); } catch { /* old browser */ } } function perfMeasure(name, start) { try { performance.measure(name, start); } catch { /* start mark missing */ } } window.__ytpPerf = () => { const out = {}; try { for (const m of performance.getEntriesByType('measure')) if (m.name.startsWith('ytp:')) out[m.name] = Math.round(m.duration); } catch { /* none */ } return out; };frontend/app.jsboot()— first line of the function body:perfMark('ytp:boot-start');. Immediately after the FIRSTrender();call insideboot()addperfMeasure('ytp:boot', 'ytp:boot-start');.frontend/app.jsrunSearchQuery— afterconst mySeq = ++searchSeq;addperfMark('ytp:search-start');. AfterRecentSearches.cacheResults(q, results);addperfMeasure('ytp:search', 'ytp:search-start');.frontend/app.jsPlayer.loadVideo— first line of the body:perfMark('ytp:tap');.frontend/app.jsmasterplayinglistener — afterif (!masterIs(el)) return;add:if (performance.getEntriesByName('ytp:tap').length) { perfMeasure('ytp:tap-to-play', 'ytp:tap'); try { performance.clearMarks('ytp:tap'); } catch { /* ignore */ } }
Out of scope / do NOT touch
- No reporting endpoint, no UI. Don't change any behaviour, only add marks/logs.
- Do not touch
runYtdlpResilientor the fallback-client logic.
Verification
cd /home/user/ytplayer && node --check frontend/app.js && cd server && bun build server.js --target=bun --outdir=/tmp/ytp-check >/dev/null && echo SERVER_OK
cd /home/user/ytplayer && node --test frontend/*.test.js 2>&1 | tail -3
grep -c "perfMark\|perfMeasure" frontend/app.js
Expected: no syntax errors, SERVER_OK, tests fail 0, grep count ≥ 8.
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: surprises, deviations from the steps, anything skipped and why.
Do not commit. Do not push. Do not touch files outside the Steps.
Execution log
- Executor: in-session Agent (haiku), gateway
delegate.shnot installed. Attempts: 1. Fix rounds: 0. - Orchestrator re-ran Verification: syntax OK,
SERVER_OK,node --test frontend/*.test.js52 pass / 0 fail, mark count 8. - Executor Findings (verbatim): All 6 plan steps applied successfully. Frontend timing marks added for boot (start->first paint), search (start->cached), tap-to-play (tap->playing event). Server logs yt-dlp duration on every invocation. Verification: syntax OK, server builds, unit tests pass (0 failures), 8 perfMark/perfMeasure occurrences (exceeds required 8). No deviations or skips.