Post.source_id refactor + tick/backfill modes + PARTIAL classifier + Mux fix + UX #44
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Five thematically distinct batches landed on
devsince the last main cut.All five are CI-green; head is
717b601.1. Nullable
Post.source_id+ denormalizedPost.artist_id(alembic 0030)ondelete=SET NULL.Post.artist_iddenormalized as NOT NULL FK CASCADE.(artist_id, external_post_id) WHERE source_id IS NULLkeeps the post-uniqueness contract for filesystem-imported content.sidecar:<platform>:<slug>synthetic Source rows that used to satisfy the old NOT NULL._lookup_source_for_sidecarreturns the real Source when a subscription exists, otherwise None. The Post just sits null-source.Post.artist_id;Sourceis now LEFT JOIN'd; dict surfaces emitsource: nullwhen absent.Postto avoid SQLAlchemy correlating to the outerprimary_post_idjoin.e.source?.platform ?? 'filesystem import'.2. Tick/backfill download modes + PARTIAL classifier (Scribe plan #544, alembic 0031)
Source.backfill_runs_remainingcounter. When> 0, downloads use gallery-dl'sskip: True+ 1800s timeout (walk full history); when0, downloads useskip: "exit:20"+ the default 870s (catch-up mode, exits gallery-dl after 20 contiguous archived items).POST /api/sources/{id}/backfillarms a source (1..10 runs, default 3). "Deep scan" button + remaining-count chip on eachSourceRow.ErrorType.PARTIALclassifies runs that downloaded ≥1 file but didn't finish in budget. Downstream maps tostatus="ok"so timeout-mid-walk no longer flips events red.3. Newly added enabled sources start in backfill mode
SourceService.create()pre-armsbackfill_runs_remaining=3whenenabled=True. Disabled-on-create sources (sidecar synthetics, deliberately-disabled adds) stay at 0.4. Mux video Referer/Origin forwarding for yt-dlp
downloader.ytdl.raw-options.http_headersblock in_get_default_confignow pinsReferer: https://www.patreon.com/+Origin: https://www.patreon.comso yt-dlp's manifest fetch clears Mux's policy.status="ok".5. Failing-sources UX
FailingSourcesCardrows get zebra striping + hover highlight. Operator-flagged: with 17 rows, eye couldn't trace from artist name on the left to Logs/Retry buttons on the right.Test plan
717b601) — six jobs, all passingsidecar:*sources in Subscriptions UI🤖 Generated with Claude Code
Routine subscription polls walked the entire post history every tick even when nothing had changed, because gallery-dl's default `skip: True` continues iterating archived posts. A creator with ~550 archived posts (Knuxy patreon) saturates the 870s wall-clock cap before completing, even with zero downloads needed. Plus, a tier-limited run that downloaded hundreds of files but ran out the clock should be a warning, not an error. Two coupled changes, both operator-flagged 2026-06-01: * **Tick mode (default, cron polls).** New `TICK_SKIP_VALUE = "exit:20"` asks gallery-dl to exit after 20 contiguous archived items. Fresh subscriptions + new-content cases still walk normally; established subscription with zero new content exits in ~30s of HEAD requests instead of pegging the timeout. 20 (not 5) gives headroom against paywall warnings interleaving with archived items. * **Backfill mode (explicit, operator-triggered).** Sticky for N runs via new `Source.backfill_runs_remaining` (alembic 0031). While > 0, downloads use `skip: True` + 1800s timeout. Auto-decrements per run with early-reset to 0 when a clean run finds zero files (queue drained). N defaults to 3 — multiple runs give the system enough budget to finish a deep walk across timeout boundaries. New `POST /api/sources/{id}/backfill` arms the source; "Deep scan" button on each SourceRow (chip shows remaining count) wires it. Plus partial-success classifier: non-zero gallery-dl exit + ≥1 file downloaded + no source-level error fires `ErrorType.PARTIAL`, which download_service maps to `status=\"ok\"`. The run did real work; the next tick continues via gallery-dl's archive. No more red events for "timed out mid-walk after downloading 300 files." Retires `SourceConfig.skip_existing` — skip value is now derived from the source state and passed as a separate `skip_value` parameter through download() / _build_config_for_source(). `GD_DEFAULTS` drops the now-dead key (was inert data after this refactor). Tests cover: * tick + backfill skip-value emission in _build_config_for_source * PARTIAL classifier branch + TIER_LIMITED-wins-over-PARTIAL ordering * SourceService.set_backfill_runs validation + persistence * /api/sources/{id}/backfill 200/400/404 paths * download_service auto-decrement / auto-reset / tick-mode-no-touch * PARTIAL → status=ok in the orchestrator (no consecutive_failures bump)Nullable Post.source_id + denormalized Post.artist_id (retire sidecar synthetics)to Post.source_id refactor + tick/backfill modes + PARTIAL classifier + Mux fix + UX