fix(clip): a clip is held to its owner's storage cap, and its limits and failures say the server's truth
- The cut file is measured against what its owner has left, for a YouTube range (whose fetch never reads yt-dlp's size ceiling) and for a Plex clip (which fetches nothing), before the fetch and again before it is placed; a refusal leaves the asset pending for a Retry. - A silent Plex file is cut silent; a file the probe cannot read says so. - A second job on a failed or renamed clip gets the same YouTube diagnosis and the fetched title. - The "too long" refusal names the server's cap; a contract test holds the client's copies of the clip limits to clip.py; the shipped player clients are pinned against the installed yt-dlp. - One HDR transfer set and one rating_key lookup; job status words from JobStatus; the display-name guard sees subscript writes and the clip route; stale comments and types follow the clip job.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// The limits a clip is marked to, held in two languages: the clip panel's handles, fields and keys
|
||||
// keep a range the server takes, and a crop frame no smaller than it cuts. Neither side can see the
|
||||
// other's copy, so the gate reads both (as `api/downloads.contract.test.ts` does for the name bound).
|
||||
// A cap moved on one side alone makes the panel offer what the server refuses — or clamp below
|
||||
// what it would take — with every other test green.
|
||||
//
|
||||
// Read as TEXT: the modules are plain constants, but reading the source is what makes a renamed or
|
||||
// moved constant fail here instead of comparing two undefineds.
|
||||
const src = (u: string) => readFileSync(new URL(u, import.meta.url), "utf-8");
|
||||
const backend = src("../../../backend/app/downloads/clip.py");
|
||||
|
||||
function pyConst(name: string): number {
|
||||
const m = backend.match(new RegExp(`^${name}\\s*=\\s*([\\d.]+)`, "m"));
|
||||
expect(m, `backend/app/downloads/clip.py no longer defines ${name}`).toBeTruthy();
|
||||
return Number(m![1]);
|
||||
}
|
||||
|
||||
function tsConst(file: string, name: string): number {
|
||||
const m = src(file).match(new RegExp(`^(?:export )?const ${name} = ([\\d.]+);`, "m"));
|
||||
expect(m, `${file} no longer defines ${name}`).toBeTruthy();
|
||||
return Number(m![1]);
|
||||
}
|
||||
|
||||
describe("clip limits", () => {
|
||||
it.each([
|
||||
["./clipRange.ts", "MIN_CLIP_S"],
|
||||
["./clipRange.ts", "MAX_CLIP_S"],
|
||||
["./cropGeometry.ts", "MIN_CROP"],
|
||||
])("%s %s is the server's", (file, name) => {
|
||||
expect(tsConst(file, name)).toBe(pyConst(name));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user