Tag-casing acronym fix, Patreon resolver hardening, archive diagnostics, post-card strip #74

Merged
bvandeusen merged 6 commits from dev into main 2026-06-06 19:59:32 -04:00
Owner

Operator-feedback batch. All CI-green on dev (run 624).

Tags

  • Acronym-safe casing (df2310b): normalize_tag_name only capitalizes the first letter of each word and preserves the rest — DC/NSFW survive (was lowercasing the tail → Dc/Nsfw). Matches ml/tag_name._title_word, so a Camie-suggested tag keeps the exact casing the suggestion UI showed when it round-trips through POST /api/tags on Accept. Lives only in the create endpoint, not the ML/allowlist path. Trade-off: all-caps input no longer folds to Title Case (protecting acronyms wins).

Patreon resolution

  • Diagnosable errors (3556a54): the three resolution surfaces (download event, dry-run preview, verify probe) now report source_url, the extracted vanity, and the exact campaigns-API lookup URL.
  • Resolver hardening (a559fab): when Patreon's /api/campaigns?filter[vanity]= returns empty (its known failure mode), fall back to scraping the campaign id from the creator page's bootstrap JSON — gallery-dl's actual method.

Imports / archives

  • "No images beside a zip" diagnostics (19eb4e9): when _import_archive captures an archive without extracting any image (probe rejected / corrupt / missing extractor backend / non-media-only), it now records {file, reason} under the event's metadata.unextracted_archives and logs a warning, instead of failing silently.

UI

  • Post-card thumbnail strip (9374f63): the hero-collage rail spans the full hero width (CSS grid, up to 5 cells + a "+N" overflow tile) instead of capping at 3 left-aligned cells.

🤖 Generated with Claude Code

Operator-feedback batch. All CI-green on dev (run 624). ## Tags - **Acronym-safe casing** (`df2310b`): `normalize_tag_name` only capitalizes the first letter of each word and preserves the rest — `DC`/`NSFW` survive (was lowercasing the tail → `Dc`/`Nsfw`). Matches `ml/tag_name._title_word`, so a Camie-suggested tag keeps the exact casing the suggestion UI showed when it round-trips through `POST /api/tags` on Accept. Lives only in the create endpoint, not the ML/allowlist path. Trade-off: all-caps input no longer folds to Title Case (protecting acronyms wins). ## Patreon resolution - **Diagnosable errors** (`3556a54`): the three resolution surfaces (download event, dry-run preview, verify probe) now report `source_url`, the extracted `vanity`, and the exact campaigns-API lookup URL. - **Resolver hardening** (`a559fab`): when Patreon's `/api/campaigns?filter[vanity]=` returns empty (its known failure mode), fall back to scraping the campaign id from the creator page's bootstrap JSON — gallery-dl's actual method. ## Imports / archives - **"No images beside a zip" diagnostics** (`19eb4e9`): when `_import_archive` captures an archive without extracting any image (probe rejected / corrupt / missing extractor backend / non-media-only), it now records `{file, reason}` under the event's `metadata.unextracted_archives` and logs a warning, instead of failing silently. ## UI - **Post-card thumbnail strip** (`9374f63`): the hero-collage rail spans the full hero width (CSS grid, up to 5 cells + a "+N" overflow tile) instead of capping at 3 left-aligned cells. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
bvandeusen added 6 commits 2026-06-06 19:59:26 -04:00
fix(posts): post-card thumbnail strip spans the full hero width
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 21s
CI / backend-lint-and-test (push) Successful in 25s
CI / integration (push) Successful in 3m1s
9374f63953
The hero collage's thumbnail rail hard-capped at 3 fixed-80px cells, left-
aligned, so it never reached the edge of the (50%-width) hero. Make the rail a
CSS grid of equal columns (1fr) at a fixed height that stretches to the hero's
full width: show up to 5 thumbnails, and when a post has more images than fit,
the last cell becomes the "+N" overflow tile (count unchanged). Column count is
driven by --fc-rail-cols so the strip always reaches the hero edge regardless
of image count.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
normalize_tag_name now only capitalizes the first letter of each word and
leaves the rest of the word untouched (was lowercasing the tail, which turned
DC→Dc / NSFW→Nsfw). This matches ml/tag_name._title_word, so a Camie-suggested
tag keeps the exact casing the suggestion UI showed when it round-trips through
POST /api/tags on Accept — addressing "auto-suggested tags must obey
capitalization" and "don't mangle acronyms" in one rule.

Trade-off (operator-chosen): all-caps input no longer folds to Title Case, so
case-variant merging in #714 still folds the dominant lowercase-vs-Title case
but leaves all-caps stylizations distinct (protecting acronyms wins). Tests
updated + a new test documenting acronym preservation / non-folding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(patreon): surface source URL + vanity in campaign-id resolution errors
CI / lint (push) Failing after 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 28s
CI / integration (push) Successful in 3m2s
3556a54260
Naming/lookup failures now report the source_url, the extracted vanity, and the
exact campaigns-API lookup URL attempted, so a "could not resolve campaign id"
error is diagnosable (wrong vanity? cookie/auth? creator renamed?) instead of
opaque. Applied to all three resolution surfaces: the native download event,
the dry-run preview, and the credential-verify probe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
style: drop aliased cross-module import (I001) — use a local _CAMPAIGNS_API constant
CI / lint (push) Successful in 3s
CI / backend-lint-and-test (push) Successful in 25s
CI / frontend-build (push) Successful in 27s
CI / integration (push) Successful in 3m0s
711fd2bb75
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(patreon): scrape creator-page HTML as a campaign-id resolution fallback
CI / lint (push) Successful in 3s
CI / backend-lint-and-test (push) Successful in 25s
CI / frontend-build (push) Successful in 27s
CI / integration (push) Successful in 3m1s
a559fabdd5
Patreon's /api/campaigns?filter[vanity]= lookup returns empty data for creators
that plainly exist (operator-flagged 2026-06-06 — Atole etc. erroring at the
resolve step). gallery-dl never used that endpoint; it pulls the campaign id out
of the creator page's bootstrap JSON. Add the same as a fallback: when the API
misses, GET the creator page (bare + /c/ vanity paths) and scrape the first
campaign id from any known embedding ("id":"…","type":"campaign" /
"campaign":{"data":{"id" / /api/campaigns/<id> / "campaign_id"). API is still
tried first (cheap, structured); the page scrape only runs on a miss.

Tests: API-empty → page-scrape fallback resolves; _scrape_campaign_id pattern
coverage. Existing API-path tests unchanged (happy paths short-circuit before
the fallback; failure paths hit the guarded scrape and still return None).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(import): surface WHY an archive was captured without extracting images
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 24s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m1s
19eb4e9388
The recurring "post shows a zip but no images" report had no diagnostic: when
_import_archive captured an archive as a bare PostAttachment — because the
bomb-guard probe rejected it, or extraction yielded zero members (corrupt /
unsupported / missing extractor backend), or it held only non-media files — it
returned status="attached" silently.

Now those paths set ImportResult.error with the specific reason and log a
warning, and download_service records each as {file, reason} under the event's
metadata.unextracted_archives (None when every archive extracted cleanly). So
the next run names exactly which archives failed and why, instead of leaving the
operator to guess. No behaviour change to the happy path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bvandeusen merged commit ef8f4f7193 into main 2026-06-06 19:59:32 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledCurator#74