fix: library paths follow the artist's slug, not the import folder's name (4244)
CI / lint (push) Failing after 2s
CI / extension-version (push) Successful in 2s
Build images / sign-extension (push) Successful in 4s
Build images / build-agent (push) Successful in 6s
CI / frontend-build (push) Successful in 23s
CI / backend-lint-and-test (push) Successful in 33s
Build images / build-web (push) Successful in 1m11s
Build images / smoke-web (push) Skipped
Build images / build-ml (push) Successful in 2m4s
Build images / promote (push) Skipped
CI / integration (push) Successful in 2m50s
CI / lint (push) Failing after 2s
CI / extension-version (push) Successful in 2s
Build images / sign-extension (push) Successful in 4s
Build images / build-agent (push) Successful in 6s
CI / frontend-build (push) Successful in 23s
CI / backend-lint-and-test (push) Successful in 33s
Build images / build-web (push) Successful in 1m11s
Build images / smoke-web (push) Skipped
Build images / build-ml (push) Successful in 2m4s
Build images / promote (push) Skipped
CI / integration (push) Successful in 2m50s
Step 1 of milestone #421. The images tree has 57 directory families for what the database says are single artists — `Conto`/`conto`, `InCaseArt`/`incaseart`, `StickySpoodge`/`Stickyspoodge`/`stickyspoodge`, and so on down to a four-way split for Pocket Ace Games. There was never a duplicate Artist row. `/api/artists/names` returns exactly one per artist. The files simply get written to two places for one row: `_copy_to_library` built its destination from `derive_subdir`, which mirrors the IMPORT tree's folder name verbatim, while the download path leaves files where the ingester wrote them — under the slug. Two writers, two conventions, one artist. This is the half that stops it re-growing, and it has to land before anything moves existing files: consolidate first and the next filesystem import out of a capitalised folder re-creates the directory that was just emptied. `canonical_subdir` replaces the top-level segment with the artist's slug and leaves everything below it alone — the post hierarchy is the downloader's business. Two deliberate pass-throughs: no resolved artist (nothing authoritative to canonicalise against) and an empty subdir (a file at the images root, whose fate is task #4247, not a side effect of this helper). `_supersede` resolves the KEPT row's artist for the same reason — a supersede rewrites `existing.path`, so writing it anywhere else would move a row back out of the tree being consolidated. ImageRecord carries `artist_id` with no relationship attribute, so that is a session lookup rather than an attribute. test_import_one_happy_path pinned the old `Alice/` destination and now pins `alice/` (rule 90). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
@@ -60,6 +60,37 @@ def derive_subdir(source_path: Path, import_root: Path) -> str:
|
||||
return str(rel) if str(rel) != "." else ""
|
||||
|
||||
|
||||
def canonical_subdir(subdir: str, artist_slug: str | None) -> str:
|
||||
"""`subdir` with its TOP-LEVEL segment replaced by the artist's slug.
|
||||
|
||||
canonical_subdir("Conto/patreon", "conto") -> "conto/patreon"
|
||||
canonical_subdir("Conto", "conto") -> "conto"
|
||||
canonical_subdir("Conto/patreon", None) -> "Conto/patreon"
|
||||
canonical_subdir("", "conto") -> ""
|
||||
|
||||
`derive_subdir` mirrors the IMPORT tree's folder names verbatim, so a
|
||||
filesystem import out of `/import/Conto/...` used to write
|
||||
`<images_root>/Conto/...` while the download path wrote `<root>/conto/...`
|
||||
for the very same Artist row. One artist, two directories, forever — 57
|
||||
such families had accumulated by 2026-09-21, and the database never had
|
||||
duplicate artists at all (milestone #421).
|
||||
|
||||
The slug is the canonical name because it is the Artist row's own
|
||||
identifier: it is what `/api/artists` reports, what the ingesters already
|
||||
write, and the one spelling that cannot vary with how a folder happened to
|
||||
be capitalised on the way in.
|
||||
|
||||
Two deliberate pass-throughs. NO artist resolved means there is nothing
|
||||
authoritative to canonicalise against, and an EMPTY subdir is a file
|
||||
landing at the images root — those have no artist folder to correct, and
|
||||
what becomes of them is its own decision (task #4247), not a side effect
|
||||
of this helper.
|
||||
"""
|
||||
if not artist_slug or not subdir:
|
||||
return subdir
|
||||
return str(Path(artist_slug, *Path(subdir).parts[1:]))
|
||||
|
||||
|
||||
def hash_suffixed_name(stem: str, sha256_hex: str, ext: str) -> str:
|
||||
"""Builds 'stem__<first10ofhash><ext>'.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user