fix(import): resolve artist from path for enrich-on-duplicate (#718)
The dedup branches of _import_media linked the existing image to the new post via _apply_sidecar(artist=None), relying on the SIDECAR to carry the artist. But an archive member's artist comes from its path, and under post-first the per-media sidecar is minimal (no artist) — so a re-packed / cross-posted archive image deduped and was left UNLINKED from the new post, i.e. the post showed "no images". Resolve the path-anchored artist (derive_top_level_artist) up-front in _import_media and pass it to both enrich-on-duplicate branches (sha256 + phash larger_exists) and the new-record path. Drop the now-dead _attach_artist helper (its logic is inlined at the single new-record call site). Surfaced by the new test_archive_all_deduped_is_benign_not_flagged (was asserting 2==4: the second post got no provenance links). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -682,6 +682,15 @@ class Importer:
|
||||
error=f"{pct:.2%} transparent",
|
||||
)
|
||||
|
||||
# Artist anchored to the attribution path (folder→artist), resolved
|
||||
# UP-FRONT so the enrich-on-duplicate branches link provenance with the
|
||||
# right artist even when the sidecar carries none — which is now the norm
|
||||
# for archive members under post-first (the per-media sidecar is minimal).
|
||||
# Without this a cross-posted / re-packed archive image deduped and was
|
||||
# left UNLINKED from the new post = a post showing "no images" (#718).
|
||||
_artist_name = derive_top_level_artist(attribution_path, self.import_root)
|
||||
path_artist = self._upsert_artist(_artist_name) if _artist_name else None
|
||||
|
||||
# Hash dedup (exact).
|
||||
sha = _sha256_of(source)
|
||||
existing_stmt = select(ImageRecord).where(ImageRecord.sha256 == sha)
|
||||
@@ -691,9 +700,9 @@ class Importer:
|
||||
# Enrich-on-duplicate (parity with attach_in_place): a re-scanned
|
||||
# file that is a byte-dup of an existing image still links its
|
||||
# post via _apply_sidecar, so a cross-posted image shows on every
|
||||
# post. Artist is derived from the sidecar here (not yet resolved).
|
||||
# post.
|
||||
self._apply_sidecar(
|
||||
existing, attribution_path, None,
|
||||
existing, attribution_path, path_artist,
|
||||
explicit_source=explicit_source,
|
||||
)
|
||||
self.session.commit()
|
||||
@@ -727,7 +736,7 @@ class Importer:
|
||||
larger = self.session.get(ImageRecord, match_id)
|
||||
if larger is not None:
|
||||
self._apply_sidecar(
|
||||
larger, attribution_path, None,
|
||||
larger, attribution_path, path_artist,
|
||||
explicit_source=explicit_source,
|
||||
)
|
||||
self.session.commit()
|
||||
@@ -761,13 +770,11 @@ class Importer:
|
||||
self.session.flush()
|
||||
self._phash_cache_append(phash, width, height, record.id)
|
||||
|
||||
# Folder→artist (anchored to attribution_path).
|
||||
artist = None
|
||||
artist_name = derive_top_level_artist(
|
||||
attribution_path, self.import_root
|
||||
)
|
||||
if artist_name:
|
||||
artist = self._attach_artist(record, artist_name)
|
||||
# Folder→artist (path_artist resolved up-front above); bind it to the
|
||||
# new record so it carries the canonical Artist.
|
||||
if path_artist is not None and record.artist_id is None:
|
||||
record.artist_id = path_artist.id
|
||||
self.session.flush()
|
||||
|
||||
# Sidecar provenance (best-effort; never fails the import).
|
||||
# explicit_source lets the FC-3c download path bind the new
|
||||
@@ -777,7 +784,7 @@ class Importer:
|
||||
# subscription-downloaded zip previously lost subscription
|
||||
# linkage if the on-disk layout didn't match assumptions.
|
||||
self._apply_sidecar(
|
||||
record, attribution_path, artist, explicit_source=explicit_source,
|
||||
record, attribution_path, path_artist, explicit_source=explicit_source,
|
||||
)
|
||||
|
||||
# Thumbnail is queued separately by the calling task; the importer
|
||||
@@ -1076,17 +1083,6 @@ class Importer:
|
||||
self.session.flush()
|
||||
return artist
|
||||
|
||||
def _attach_artist(self, record: ImageRecord, artist_name: str) -> Artist:
|
||||
"""Upsert the Artist and set it as the image's canonical artist.
|
||||
Artist-kind tags were retired in FC-2d-vii-c — the Artist row is
|
||||
the single source of truth. Returns the Artist (sidecar
|
||||
provenance attaches its Source to it)."""
|
||||
artist = self._upsert_artist(artist_name)
|
||||
if record.artist_id is None:
|
||||
record.artist_id = artist.id
|
||||
self.session.flush()
|
||||
return artist
|
||||
|
||||
@staticmethod
|
||||
def _sidecar_artist_name(data: dict) -> str | None:
|
||||
for k in ("artist", "author_name"):
|
||||
|
||||
Reference in New Issue
Block a user