fix(pixiv): capture ugoira frame timings in the post record (ordering bug)
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 28s
CI / integration (push) Successful in 3m33s

The core writes the post record BEFORE extract_media, but the ugoira frame
delays were only memoized DURING extract_media — so write_post_record never saw
them and ugoira_frames was always empty in the record. Extract a memoized
_ugoira_meta (frames + zip url share ONE /v1/ugoira/metadata call regardless of
order) and inject client.fetch_ugoira_frames into the downloader (mirrors
Patreon's content_fetcher) so write_post_record populates the frames itself.
Zero extra API calls — the fetch is shared/memoized with extract_media. A
recapture now backfills the timings onto existing ugoira posts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
2026-07-03 19:41:29 -04:00
parent 6c6e8bdb6d
commit f33808b977
5 changed files with 130 additions and 11 deletions
+34
View File
@@ -268,6 +268,40 @@ def test_write_post_record_includes_ugoira_frames(tmp_path):
assert data["ugoira_frames"] == frames
def test_write_post_record_fetches_ugoira_frames_when_absent(tmp_path):
# The core writes the post record BEFORE extract_media, so frames aren't
# memoized yet — the injected fetcher must populate them so the record keeps
# the timings (regression: they were silently always empty).
post = _post_only(333)
assert "_ugoira_frames" not in post["_work"]
frames = [{"file": "000000.jpg", "delay": 120}]
calls = []
def fetcher(p):
calls.append(p)
p["_work"]["_ugoira_frames"] = frames
dl = PixivDownloader(
tmp_path, validate=False, session=_FakeSession(),
ugoira_frames_fetcher=fetcher,
)
outcome = dl.write_post_record(post, "artist-a")
data = json.loads(outcome.path.read_text())
assert data["ugoira_frames"] == frames
assert len(calls) == 1
def test_write_post_record_non_ugoira_does_not_call_fetcher(tmp_path):
post = _post_only(111) # a plain multi-page illust
calls = []
dl = PixivDownloader(
tmp_path, validate=False, session=_FakeSession(),
ugoira_frames_fetcher=lambda p: calls.append(p),
)
dl.write_post_record(post, "artist-a")
assert calls == []
def test_write_post_record_without_id(tmp_path):
dl = _downloader(tmp_path)
outcome = dl.write_post_record({"id": None, "attributes": {}}, "artist-a")