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
+37
View File
@@ -740,6 +740,30 @@ def test_the_thirty_second_gif_cap_itself_is_allowed():
assert clipmod.duration(clipmod.normalize("plex", "42", 0, 30, gif_side=480)) == 30
@pytest.mark.parametrize(
("start", "end", "kw"),
[
# Each a range exactly at a cap whose float difference lands a hair past it.
(30.529, 60.529, {"gif_side": 480}),
(424.005, 1024.005, {}),
(0.001, 1.001, {}),
],
)
def test_a_range_exactly_at_a_cap_is_judged_in_milliseconds(start, end, kw):
assert end - start not in (1, 30, 600) # the float difference is off the cap itself
assert round((end - start) * 1000) in (1000, 30000, 600000)
clipmod.normalize("youtube", "abcdefghijk", start, end, **kw)
def test_a_gif_clip_keeps_no_audio_track_so_the_track_does_not_make_another_gif():
first = clipmod.normalize("plex", "42", 10, 20, audio_track=0, gif_side=480)
other = clipmod.normalize("plex", "42", 10, 20, audio_track=1, gif_side=480)
assert "audio_track" not in first
assert clipmod.sig(first) == clipmod.sig(other)
# A video clip keeps the track it was heard on.
assert clipmod.normalize("plex", "42", 10, 20, audio_track=1)["audio_track"] == 1
def test_a_gif_clip_is_queued_as_a_file_and_too_long_says_its_own_cap(
db, make_user, as_user, yt_video
):
@@ -810,6 +834,19 @@ def test_a_gif_clip_is_cut_through_the_gif_command_and_stored_as_an_image_file(
assert cmd[cmd.index("-t") + 1] == "15.000"
def test_a_gif_clip_is_never_refused_for_an_audio_track(
db, make_user, as_user, plex_episode, gif_worker
):
# A GIF queued with a track (as one made before GIFs dropped it was): the file has one track,
# the spec names a fourth, and a GIF, which has no sound, is made all the same.
job = _clip(as_user(make_user()), source="plex", ref="777", gif_side=480, end_s=25).json()
row = db.get(DownloadJob, job["id"])
row.edit_spec = {**row.edit_spec, "audio_track": 3}
db.commit()
job, asset = gif_worker["run"](job)
assert (job.status, asset.status) == ("done", "ready")
def test_a_gif_over_its_size_cap_is_not_kept_and_says_why(
db, make_user, as_user, plex_episode, gif_worker, monkeypatch
):