Files
hyperframes/packages/studio-server/package-subpaths.json
Miguel ÁngelandHuntsDesk c54f84859d fix(studio-server): long media clips no longer leave the host unanswered (#7002)
* fix(studio-server): more than six clips on one media file no longer starve check and preview of connections

Every file server answered `Range: bytes=N-` with the rest of the file. A
media element reads that until its buffer is full, then stops reading but
keeps the response open, so each <video> held one of Chrome's six HTTP/1.1
connections to the host. With seven or more <video> clips on the server's
host, every other request queued behind them and `hyperframes check`
stalled at 0% CPU. A long recording cut into trimmed segments, each its own
<video> on the same file, is enough.

An open-ended range of audio or video now gets at most 4 MiB
(OPEN_MEDIA_RANGE_BYTES), as a normal 206 with an exact Content-Range. The
element asks for the next range as it plays or seeks. Bounded and suffix
ranges, and files that aren't media, are served as before. The helper lives
in studio-server; its preview route and the CLI's static project server and
composition server use it.

Fixes #6029

Claude-Session: https://claude.ai/code/session_014aw1z3VrTNenka3LGDBBLa

* fix(studio-server): carry the range cap's reason in names and tests, not comments

The comment ratchet doesn't let a file's comment share rise: mediaRange.ts
was 48% comments against the package's 7%, and staticProjectServer.ts gained
a line. The cap is now MAX_OPEN_MEDIA_RANGE_BYTES, and the why and the
repro stay in the three tests and #6029.

Claude-Session: https://claude.ai/code/session_014aw1z3VrTNenka3LGDBBLa

* fix(producer): the render file server bounds open-ended media ranges too

parseRangeHeader takes the response's content type and caps an open-ended
`bytes=N-` on audio or video with openRangeEnd, like the CLI and Studio
servers. The producer imports it from a new `media-range` subpath of
@hyperframes/studio-server, as it does `encoder-options`, so a render
doesn't load the rest of the package.

Claude-Session: https://claude.ai/code/session_01DW4V8HZZx5JUgMffb3juVV

* test(cli): eight clips on one file no longer leave the host unanswered

A real Chromium page buffers eight long video clips of one file from the project server and then asks the same host for another file; with open-ended ranges unbounded the request stays unanswered, with the bounded piece it is answered at once.

* docs(producer): range comments name the open-ended form in lowercase

* docs(cli): the range test's comment says what holds now

---------

Co-authored-by: HuntsDesk <hunter@talloak.com>
2026-11-02 11:03:55 +00:00

109 lines
3.8 KiB
JSON

{
"package": "@hyperframes/studio-server",
"localNodeRuntime": true,
"subpaths": {
".": {
"source": "./src/index.ts",
"runtime": "./dist/index.js",
"types": "./dist/index.d.ts",
"environments": ["bun", "node"]
},
"./package.json": {
"source": "./package.json",
"runtime": "./package.json",
"types": null,
"environments": ["bun", "node"]
},
"./screenshot-clip": {
"source": "./src/helpers/screenshotClip.ts",
"runtime": "./dist/helpers/screenshotClip.js",
"types": "./dist/helpers/screenshotClip.d.ts",
"environments": ["bun", "node"]
},
"./safe-path": {
"source": "./src/helpers/safePath.ts",
"runtime": "./dist/helpers/safePath.js",
"types": "./dist/helpers/safePath.d.ts",
"environments": ["bun", "node"]
},
"./hf-ids": {
"source": "./src/helpers/hfIdPersist.ts",
"runtime": "./dist/helpers/hfIdPersist.js",
"types": "./dist/helpers/hfIdPersist.d.ts",
"environments": ["bun", "node"]
},
"./history": {
"source": "./src/history/index.ts",
"runtime": "./dist/history/index.js",
"types": "./dist/history/index.d.ts",
"environments": ["bun", "node"]
},
"./manual-edits-render-script": {
"source": "./src/helpers/manualEditsRenderScript.ts",
"runtime": "./dist/helpers/manualEditsRenderScript.js",
"types": "./dist/helpers/manualEditsRenderScript.d.ts",
"environments": ["bun", "node"]
},
"./studio-motion-render-script": {
"source": "./src/helpers/studioMotionRenderScript.ts",
"runtime": "./dist/helpers/studioMotionRenderScript.js",
"types": "./dist/helpers/studioMotionRenderScript.d.ts",
"environments": ["bun", "node"]
},
"./draft-markers": {
"source": "./src/helpers/draftMarkers.ts",
"runtime": "./dist/helpers/draftMarkers.js",
"types": "./dist/helpers/draftMarkers.d.ts",
"environments": ["browser", "bun", "node"]
},
"./finite-mutation": {
"source": "./src/helpers/finiteMutation.ts",
"runtime": "./dist/helpers/finiteMutation.js",
"types": "./dist/helpers/finiteMutation.d.ts",
"environments": ["bun", "node"]
},
"./source-mutation": {
"source": "./src/helpers/sourceMutation.ts",
"runtime": "./dist/helpers/sourceMutation.js",
"types": "./dist/helpers/sourceMutation.d.ts",
"environments": ["browser", "bun", "node"]
},
"./media-codec-map": {
"source": "./src/helpers/mediaCodecMap.ts",
"runtime": "./dist/helpers/mediaCodecMap.js",
"types": "./dist/helpers/mediaCodecMap.d.ts",
"environments": ["bun", "node"]
},
"./proxy-transcoder": {
"source": "./src/helpers/proxyTranscoder.ts",
"runtime": "./dist/helpers/proxyTranscoder.js",
"types": "./dist/helpers/proxyTranscoder.d.ts",
"environments": ["bun", "node"]
},
"./loudness": {
"source": "./src/helpers/loudness.ts",
"runtime": "./dist/helpers/loudness.js",
"types": "./dist/helpers/loudness.d.ts",
"environments": ["bun", "node"]
},
"./media-proxy-preview": {
"source": "./src/helpers/mediaProxyPreview.ts",
"runtime": "./dist/helpers/mediaProxyPreview.js",
"types": "./dist/helpers/mediaProxyPreview.d.ts",
"environments": ["bun", "node"]
},
"./encoder-options": {
"source": "./src/helpers/encoderOptions.ts",
"runtime": "./dist/helpers/encoderOptions.js",
"types": "./dist/helpers/encoderOptions.d.ts",
"environments": ["bun", "node"]
},
"./media-range": {
"source": "./src/helpers/mediaRange.ts",
"runtime": "./dist/helpers/mediaRange.js",
"types": "./dist/helpers/mediaRange.d.ts",
"environments": ["bun", "node"]
}
}
}