Skip to content

fix(preview): the proxy copy for the video at the playhead is made ahead of the rest - #6586

Merged
miguel-heygen merged 4 commits into
mainfrom
fix/proxy-playhead-first
Oct 27, 2026
Merged

miguel-heygen merged 4 commits into
mainfrom
fix/proxy-playhead-first

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

What

When the preview waits for a video's proxy copy (a codec the browser cannot decode, or a lighter copy), the request for the video that is at the playhead now carries X-HF-Proxy-Priority: 1. The preview route passes that to the transcoder's existing priority option, which queues that copy ahead of the rest and never refuses it for a full queue.

Why

A project of many HEVC clips on a machine with no HEVC decoder shows only the captions over black until the proxies are made. The server pre-warms every copy in project order and transcodes two at a time with a bounded queue, so the clip the person is looking at waited behind the others, and once the queue was full its request got a 503 and retried every 5 s with the rest. The priority option was written for exactly this ("the copy a viewer is waiting on") but no route ever set it.

Measured on a Linux box with 24 portrait HEVC clips (8 s each): the first picture came after about 20 to 30 s and all 24 copies after about 53 to 65 s, with nothing on screen saying why. This change does not shorten the total; it makes the video under the playhead the first one made, including after a jump to another clip.

How

isActiveAtPlayhead in media.ts reads the sync state the tick already keeps for a clip inside its window. waitForServedProxy sends the header on each ask while that holds, so a clip the playhead leaves stops asking for priority and one it enters starts. On the server the copy asked for last goes first, and a repeat ask moves a queued copy to the front, so after several jumps the clip under the playhead is still the one made next. The header is on the poll, not the URL, so the element's src and the cached copy's etag are unchanged.

Tests

  • mediaProxy.test.ts: the ask carries the header while the clip is at the playhead and not after the playhead leaves.
  • preview.test.ts: the header becomes priority: true; the existing exact-argument assertions now include { priority: false }.
  • proxyTranscoder.test.ts: the copy asked for last runs first, and a repeat ask moves it to the front.
  • Each new test fails with its fix disabled (header never sent, header ignored, old queue order) and passes with it.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits · b9bfce6f

The priority plumbing works. Changed suites pass locally (core mediaProxy.test.ts 63/63, studio-server preview.test.ts + proxyTranscoder 218/218). The only blocker is the red Comments run. ↓ inline

Scope

Read all 8 files in full, plus proxyTranscoder.ts resolveProxy/prioritize/enqueue and the lastRelativeTime writers in media.ts. Adding a custom request header could force a CORS preflight, but that isn't a new risk: the existing X-HF-Proxy-Progress response header already assumes same-origin. Several CI workflows were still running when I posted this. No prior reviews.

— Genesis

Comment thread packages/core/src/previewProxyBox.ts Outdated

export const PREVIEW_PROXY_RETRY_PARAM = "hf-proxy-retry";
export const PREVIEW_PROXY_PROGRESS_HEADER = "X-HF-Proxy-Progress";
/** Sent with "1" while the video is at the playhead: its copy goes ahead of the rest in the queue. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker: the Comments workflow is red on this line: RAISED comment share: packages/core/src/previewProxyBox.ts is 6.8% (5/73), at the base 5.6% (4/71). Drop the line; the constant's name and its use in proxyWait.ts already say what it does.

Suggested change
/** Sent with "1" while the video is at the playhead: its copy goes ahead of the rest in the queue. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Fixed in 3aa9484

retryProxyCopy(project.dir, file, proxyVariant, proxyBox);
}
servedPath = await waitForProxy(resolveProxy(project.dir, file, proxyVariant, proxyBox), 0);
const priority = c.req.header(PREVIEW_PROXY_PRIORITY_HEADER) === "1";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important: priority is sticky on the server. prioritize() never demotes a job, and enqueue() puts a new priority job behind the priority jobs already queued. So the body's claim that "a clip the playhead leaves stops asking for priority" only holds on the client. Failure case: paused, you rest on clips C, D and E for one poll each (≥2 s), then land on F while both slots are busy. F now queues behind C, D and E, so the copy under the playhead is no longer made first. A single jump is fine. If you want the claim to hold after several jumps, two fixes would work: make the newest priority ask go to the front (waitQueue.unshift for priority), or demote a job when a later ask for it arrives without the header. Otherwise, narrow the PR body to match what the code does.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Fixed in 3aa9484

@github-actions

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 135 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (1)

  • seqplainkeys-none-center-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 2 resolved, 0 open, 0 new · b9bfce6f → 3aa94847. All runs at this head are green, including Comments. The new queue-order test fails when the old enqueue is restored.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit bff3900 Oct 27, 2026
84 checks passed
@miguel-heygen
miguel-heygen deleted the fix/proxy-playhead-first branch October 27, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant