fix(recapture): link on-disk images to their post (#1288)
Recapture disk-skips already-downloaded media, and upsert_post_record only writes Post fields — so a pre-existing image (e.g. one pulled under the old gallery-dl path, imported bare with no post) stays orphaned even after its post record is (re)written. Confirmed on the operator's instance: 329 pixiv images with primary_post_id NULL, 694 pixiv posts with content but no linked images, 0 duplicate posts. Fix: the recapture relink channel now carries the media's post_id (2- → 3-tuple path/url/post_id), and phase 3 calls importer.link_existing_image_to_post — match the on-disk image by path, find its Post by (source, external_post_id), upsert image_provenance + primary_post_id. Factored the provenance-linking out of _apply_sidecar into a shared _attach_provenance so the fresh-import and recapture-backlink paths can't diverge. Idempotent; generic across native platforms (no-op for already-linked Patreon/SubscribeStar). Re-running recapture now repairs orphaned images; future walks never orphan. 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:
@@ -418,7 +418,8 @@ class DownloadService:
|
||||
# the duplicate file). Empty outside recapture mode.
|
||||
relink_pairs = getattr(dl_result, "relink_source_paths", None) or []
|
||||
relinked = 0
|
||||
for rel_str, rel_url in relink_pairs:
|
||||
post_linked = 0
|
||||
for rel_str, rel_url, rel_post_id in relink_pairs:
|
||||
rel_path = Path(rel_str)
|
||||
if not rel_path.exists(): # noqa: ASYNC240
|
||||
continue
|
||||
@@ -428,13 +429,27 @@ class DownloadService:
|
||||
|
||||
if await loop.run_in_executor(None, _relink):
|
||||
relinked += 1
|
||||
|
||||
# #1288: link the on-disk image to its Post. Recapture disk-skips the
|
||||
# media (never re-imported), so a pre-existing image (e.g. one pulled
|
||||
# under the old gallery-dl path) otherwise stays orphaned even after
|
||||
# the post record is written. Idempotent for already-linked images.
|
||||
def _link(p=rel_path, pid=rel_post_id):
|
||||
return self.importer.link_existing_image_to_post(
|
||||
p, pid, source=source_row, artist=artist,
|
||||
)
|
||||
|
||||
if await loop.run_in_executor(None, _link):
|
||||
post_linked += 1
|
||||
if relink_pairs:
|
||||
# recapture diagnostic: how many on-disk images got their
|
||||
# source_filehash backfilled (inline-image localization). < total is
|
||||
# normal — files already carrying a filehash are skipped (NULL-only).
|
||||
# source_filehash backfilled (inline-image localization) and how many
|
||||
# got (re)linked to their Post. < total for source_filehash is normal
|
||||
# (files already carrying a filehash are skipped, NULL-only).
|
||||
log.info(
|
||||
"recap: relinked source_filehash on %d/%d on-disk image(s)",
|
||||
relinked, len(relink_pairs),
|
||||
"recap: relinked source_filehash on %d and linked %d/%d on-disk "
|
||||
"image(s) to their post",
|
||||
relinked, post_linked, len(relink_pairs),
|
||||
)
|
||||
|
||||
# Kick the off-platform file-host downloader for any links this run
|
||||
|
||||
Reference in New Issue
Block a user