fix(downloads): GIF review round 1 — shape, HDR, caps, audio, copy

- A GIF squares its pixels before the size, so an anamorphic source keeps
  its shown shape (a 720x480 DVD at 16:9 was 480x320).
- An editor GIF of an HDR upload is tonemapped, as a clip GIF is.
- The length caps are judged in whole milliseconds: a range exactly at a
  cap is no longer refused for a float hair over it.
- A GIF is made of a video only: an audio file is refused with its reason
  and the editor offers no GIF for it.
- A GIF clip keeps no audio track: the track neither splits its cache key
  nor fails it.
- The editor's GIF notes keep their live region mounted; one size reads
  as one ("about 1 MB"), none is shown with nothing kept; the size choices
  show their unit; a shared picture that fails to load says so.
- Copy: the two-sentence GIF notes keep their last stop (a copy-rule test
  now holds it); the English warns of "only a still picture".
This commit is contained in:
2026-11-12 23:24:25 +01:00
parent 0b92b40db5
commit 8845d1aa49
23 changed files with 403 additions and 78 deletions
+15
View File
@@ -33,6 +33,21 @@ describe("estimateGif", () => {
});
});
describe("gifEstimateText", () => {
it("says whether both ends read the same, so a panel says one size", () => {
expect(gifEstimateText(estimateGif(1, 480, 16 / 9), "en")).toEqual({
lo: "1",
hi: "1",
single: true,
});
expect(gifEstimateText(estimateGif(10, 480, 16 / 9), "en")).toEqual({
lo: "4",
hi: "10",
single: false,
});
});
});
describe("gifVerdict", () => {
it("refuses past the length cap, whatever the size", () => {
expect(gifVerdict(30.2, { hi: 1 })).toBe("too_long");
+3 -2
View File
@@ -41,11 +41,12 @@ export function estimateGif(
}
/** An estimate's ends as whole megabytes in the reader's number format (at least 1, as a GIF of a
* second or two still takes one in the reader's eyes). */
* second or two still takes one in the reader's eyes). `single` when both ends read the same: the
* panels then say one number ("about 1 MB"), not a range of one ("about 1–1 MB"). */
export function gifEstimateText(est: { lo: number; hi: number }, lang: string) {
const lo = Math.max(1, Math.round(est.lo / MB));
const hi = Math.max(lo, Math.round(est.hi / MB));
return { lo: formatNumber(lo, lang), hi: formatNumber(hi, lang) };
return { lo: formatNumber(lo, lang), hi: formatNumber(hi, lang), single: lo === hi };
}
/** What the panels say about a GIF before it is made: too long (refused, it does not start), it may