fix(pixiv): capture ugoira frame timings in the post record (ordering bug)
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:
@@ -200,6 +200,38 @@ def test_extract_media_ugoira_zip_swap(client, page1, monkeypatch):
|
||||
assert post["_work"]["_ugoira_frames"] == frames
|
||||
|
||||
|
||||
def test_fetch_ugoira_frames_memoizes_and_shares_one_call(client, page1, monkeypatch):
|
||||
# fetch_ugoira_frames (called by write_post_record, which the core runs
|
||||
# BEFORE extract_media) populates frames; extract_media then REUSES the
|
||||
# memoized metadata — exactly one /v1/ugoira/metadata call total.
|
||||
frames = [{"file": "000000.jpg", "delay": 90}]
|
||||
calls = []
|
||||
|
||||
def fake_call(endpoint, params):
|
||||
calls.append(endpoint)
|
||||
return {"ugoira_metadata": {
|
||||
"zip_urls": {"medium": (
|
||||
"https://i.pximg.net/img-zip-ugoira/x/333_ugoira600x600.zip"
|
||||
)},
|
||||
"frames": frames,
|
||||
}}
|
||||
|
||||
monkeypatch.setattr(client, "_call", fake_call)
|
||||
post = _post_for(client, page1, 333)
|
||||
client.fetch_ugoira_frames(post)
|
||||
assert post["_work"]["_ugoira_frames"] == frames
|
||||
items = client.extract_media(post, {}) # reuses memoized meta
|
||||
assert items[0].media_id == "ugoira"
|
||||
assert calls == ["/v1/ugoira/metadata"] # ONE fetch, not two
|
||||
|
||||
|
||||
def test_fetch_ugoira_frames_noop_for_non_ugoira(client, page1, monkeypatch):
|
||||
monkeypatch.setattr(client, "_call", lambda e, p: pytest.fail("should not fetch"))
|
||||
post = _post_for(client, page1, 222) # a single-page illust
|
||||
client.fetch_ugoira_frames(post)
|
||||
assert "_ugoira_frames" not in post["_work"]
|
||||
|
||||
|
||||
def test_extract_media_ugoira_metadata_failure_downgrades(
|
||||
client, page1, monkeypatch
|
||||
):
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user