fix(subscriptions): hide sidecar synthetic Sources + prefer real on lookup
Two coupled bugs surfaced 2026-05-31 by the Subscriptions UI showing "phantom" subscriptions like `sidecar:patreon:dpmaker`: 1. `SourceService.list()` returned every Source, no filter on URL. alembic 0022 (2026-05-26) consolidated old per-post-URL Sources into one canonical row per (artist, platform); when no real campaign URL was salvageable it rewrote the canonical to `sidecar:<plat>:<slug>` enabled=false as a disabled anchor. The UI then listed those anchors as if they were polls — disabled, but visible. Fix: `list()` excludes `url LIKE 'sidecar:%'` by default; `include_synthetic=True` opts back in for admin tooling. 2. `importer._source_for_sidecar` picked the lowest-id Source for (artist, platform). When alembic 0022 had rewritten a per-post row into a synthetic anchor (lower id) AND the operator later added the real subscription (higher id), every gallery-dl download silently attached its Post to the SYNTHETIC instead of the real Source. Fix: prefer a non-`sidecar:%` URL when one exists; fall back to the synthetic; only create a new synthetic when nothing exists for (artist, platform). alembic 0028 is the data half: for every (artist, platform) with both a synthetic AND a real Source, pre-merge Post+ImageProvenance collisions on the canonical, bulk-repoint Posts/ImageProvenance/ DownloadEvent.source_id onto the real Source, and delete the synthetic. Lone synthetics (no real twin) are left intact — they anchor real imported content the operator may still want; the list-filter hides them so they no longer surface as phantoms.
This commit is contained in:
@@ -210,3 +210,36 @@ def test_source_for_sidecar_distinct_platforms_distinct_anchors(
|
||||
assert p.id != x.id
|
||||
assert p.platform == "patreon"
|
||||
assert x.platform == "pixiv"
|
||||
|
||||
|
||||
def test_source_for_sidecar_prefers_real_over_synthetic_when_both_exist(
|
||||
importer, artist_row, db_sync,
|
||||
):
|
||||
"""When BOTH a synthetic anchor AND a real Source exist for the same
|
||||
(artist, platform), the resolver must return the REAL one. This is the
|
||||
fix for the 2026-05-31 phantom-subscription bug: alembic 0022 had
|
||||
rewritten an older per-post Source row into a sidecar synthetic, and
|
||||
the operator later added the real subscription. The old `ORDER BY
|
||||
id ASC LIMIT 1` lookup picked the older synthetic (lower id),
|
||||
silently attaching every gallery-dl download to the wrong Source.
|
||||
"""
|
||||
synthetic = Source(
|
||||
artist_id=artist_row.id, platform="patreon",
|
||||
url=f"sidecar:patreon:{artist_row.slug}", enabled=False,
|
||||
)
|
||||
db_sync.add(synthetic)
|
||||
db_sync.flush()
|
||||
real = Source(
|
||||
artist_id=artist_row.id, platform="patreon",
|
||||
url="https://www.patreon.com/testartist", enabled=True,
|
||||
)
|
||||
db_sync.add(real)
|
||||
db_sync.flush()
|
||||
assert synthetic.id < real.id # ordering precondition
|
||||
|
||||
resolved = importer._source_for_sidecar(
|
||||
artist_id=artist_row.id, platform="patreon",
|
||||
artist_slug=artist_row.slug,
|
||||
)
|
||||
assert resolved.id == real.id
|
||||
assert resolved.url == "https://www.patreon.com/testartist"
|
||||
|
||||
Reference in New Issue
Block a user