fix(sidecar): strip gallery-dl 'NN_' numbering prefix when locating sidecars — fixes 'deep scan refresh count high but 0 Posts created' (operator-flagged 2026-05-26) — Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

This commit is contained in:
2026-05-26 00:27:43 -04:00
parent efb142239d
commit 0978fbac66
2 changed files with 64 additions and 0 deletions
+19
View File
@@ -4,6 +4,7 @@ No per-platform branching: a small common key set with fallbacks; the
full JSON is kept in raw so anything unmapped is recoverable later.
"""
import re
from dataclasses import dataclass
from datetime import UTC, datetime
from pathlib import Path
@@ -21,10 +22,28 @@ class SidecarData:
raw: dict
# gallery-dl prefixes media filenames with `NN_` for in-post ordering
# (`01_HOLLOW-ICHIGO.png`, `02_HOLOW ICHIGO.zip`) but writes the sidecar
# under the attachment's stem WITHOUT that ordering prefix
# (`HOLLOW-ICHIGO.json`). Strip the prefix when looking for sidecars.
# Confirmed against real Patreon downloads 2026-05-26 — without this
# strip, every gallery-dl post-level sidecar was invisible to FC since
# FC-3 shipped (24 deep-refresh calls produced 0 Posts in operator's DB).
_NUMBERING_PREFIX = re.compile(r"^\d+_(.+)$")
def find_sidecar(media: Path) -> Path | None:
# Attachment-level sidecars (image.jpg.json, image.json).
for cand in (media.with_suffix(".json"), Path(str(media) + ".json")):
if cand.is_file():
return cand
# gallery-dl post-numbered convention: strip the `NN_` prefix from
# the stem and look for that.json in the same directory.
m = _NUMBERING_PREFIX.match(media.stem)
if m:
cand = media.parent / f"{m.group(1)}.json"
if cand.is_file():
return cand
return None
+45
View File
@@ -23,6 +23,51 @@ def test_find_sidecar_none(tmp_path):
assert find_sidecar(media) is None
def test_find_sidecar_gallerydl_numbered_prefix(tmp_path):
"""gallery-dl prefixes media filenames with NN_ for in-post ordering
(e.g. `01_HOLLOW-ICHIGO.png`) but writes the post-level sidecar under
the attachment stem WITHOUT the prefix (`HOLLOW-ICHIGO.json`).
Confirmed against real Patreon downloads 2026-05-26 — operator's deep
scan produced 24 refresh calls but 0 Posts because the unprefixed
sidecar was invisible to find_sidecar."""
media = tmp_path / "01_HOLLOW-ICHIGO.png"
media.write_bytes(b"x")
sc = tmp_path / "HOLLOW-ICHIGO.json"
sc.write_text("{}")
assert find_sidecar(media) == sc
def test_find_sidecar_multidigit_prefix(tmp_path):
"""Numbering prefix can be wider than 2 digits (`001_...`); the strip
handles any \\d+_ form."""
media = tmp_path / "001_mirko-sketch.png"
media.write_bytes(b"x")
sc = tmp_path / "mirko-sketch.json"
sc.write_text("{}")
assert find_sidecar(media) == sc
def test_find_sidecar_prefers_attachment_level_over_post_level(tmp_path):
"""If BOTH a per-attachment sidecar and a post-level sidecar exist,
the attachment-level one wins (it's more specific)."""
media = tmp_path / "01_image.png"
media.write_bytes(b"x")
per_attachment = tmp_path / "01_image.json"
per_attachment.write_text('{"specific": true}')
post_level = tmp_path / "image.json"
post_level.write_text('{"specific": false}')
assert find_sidecar(media) == per_attachment
def test_find_sidecar_no_underscore_not_treated_as_prefix(tmp_path):
"""`01.png` (just digits, no underscore-separated stem) shouldn't
match. The regex requires NN_<something>."""
media = tmp_path / "01.png"
media.write_bytes(b"x")
(tmp_path / ".json").write_text("{}") # would be matched only if buggy
assert find_sidecar(media) is None
def test_parse_empty_dict_all_none():
sd = parse_sidecar({})
assert isinstance(sd, SidecarData)