Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 27 additions & 20 deletions backend/modules/poster_self_heal.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ def _is_under(path: str, base_dir: str) -> bool:
return p == base or p.startswith(base + os.sep)


def drive_twins(cl2k) -> tuple:
"""``(drive_ids, twin_of)``; ``twin_of(image_type)`` resolves to the first
``gdrive_uploads`` entry claiming that type, else the poster Drive, else the
first Drive."""
drive_ids: list = []
by_type: dict = {}
for drive in getattr(cl2k, "gdrive_uploads", None) or []:
fid = (getattr(drive, "folder_id", "") or "").strip()
if not fid:
continue
if fid not in drive_ids:
drive_ids.append(fid)
for image_type in getattr(drive, "types", None) or []:
by_type.setdefault(image_type, fid)
fallback = by_type.get("poster") or (drive_ids[0] if drive_ids else None)

def twin_of(image_type):
return by_type.get(image_type or "poster", fallback)

return drive_ids, twin_of


class PosterSelfHeal(ChubModule):
"""Detect stale ids / changed titles / missing ids on CL2K posters."""

Expand All @@ -61,24 +83,7 @@ def run(self) -> None:
p = (getattr(folder, "path", "") or "").strip()
if p and p not in local_dirs:
local_dirs.append(p)
drive_ids: list = []
poster_twin_id = None
for drive in getattr(cl2k, "gdrive_uploads", None) or []:
fid = (getattr(drive, "folder_id", "") or "").strip()
if not fid:
continue
if fid not in drive_ids:
drive_ids.append(fid)
# Where a locally-saved poster's Drive twin lives: the first Drive
# that receives posters (fall back to the first Drive at all, which
# matches the pre-redesign behaviour of healing the linked folder
# even with uploads off).
if poster_twin_id is None and "poster" in (
getattr(drive, "types", None) or []
):
poster_twin_id = fid
if poster_twin_id is None and drive_ids:
poster_twin_id = drive_ids[0]
drive_ids, twin_of = drive_twins(cl2k)
if not local_dirs and not drive_ids:
self.logger.error(
"CL2K maker has no local folders or Drive uploads configured — "
Expand Down Expand Up @@ -137,8 +142,10 @@ def run(self) -> None:
seen_names.add(base)
drive_posters.append((parsed, fid))

# Local posters heal their Drive twin (if any) in poster_twin_id.
posters = [(p, poster_twin_id) for p in local_posters] + drive_posters
# Local rows heal the Drive twin that receives their own image_type.
posters = [
(p, twin_of(p.get("image_type"))) for p in local_posters
] + drive_posters
media_index = index_media(db.media.get_all())
reviews = poster_heal_review_for(db)

Expand Down
71 changes: 71 additions & 0 deletions tests/test_poster_self_heal_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,77 @@ def test_is_under_empty_inputs_are_false():
assert not _is_under(None, "/base")


# --- a local row heals in the Drive that receives ITS image_type ---

from backend.modules.poster_self_heal import drive_twins # noqa: E402


class _Drive:
def __init__(self, folder_id, types):
self.folder_id = folder_id
self.types = types


class _Cl2k:
def __init__(self, gdrive_uploads):
self.gdrive_uploads = gdrive_uploads


_ROUTED = _Cl2k(
[
_Drive("POSTERS", ["poster"]),
_Drive("BACKGROUNDS", ["background"]),
_Drive("LOGOS", ["logo"]),
_Drive("SQUAREART", ["squareart"]),
]
)


def test_twin_follows_the_drive_claiming_that_image_type():
drive_ids, twin_of = drive_twins(_ROUTED)
assert drive_ids == ["POSTERS", "BACKGROUNDS", "LOGOS", "SQUAREART"]
# The regression: art types must NOT all resolve to the poster Drive, or the
# rename targets a folder the file isn't in, raises, and never heals.
assert twin_of("poster") == "POSTERS"
assert twin_of("background") == "BACKGROUNDS"
assert twin_of("logo") == "LOGOS"
assert twin_of("squareart") == "SQUAREART"


def test_twin_falls_back_to_poster_drive_for_unclaimed_type():
_, twin_of = drive_twins(_ROUTED)
# No Drive claims "banner"; a missing image_type means poster.
assert twin_of("banner") == "POSTERS"
assert twin_of(None) == "POSTERS"


def test_twin_falls_back_to_first_drive_when_nothing_claims_posters():
# Uploads-off / asset-only setup: keep healing the linked folder.
_, twin_of = drive_twins(_Cl2k([_Drive("ONLY", [])]))
assert twin_of("poster") == "ONLY"
assert twin_of("logo") == "ONLY"


def test_twin_is_none_and_ids_empty_without_drives():
drive_ids, twin_of = drive_twins(_Cl2k([]))
assert drive_ids == []
assert twin_of("logo") is None


def test_twin_skips_blank_folder_ids_and_keeps_first_claimer():
drive_ids, twin_of = drive_twins(
_Cl2k(
[
_Drive(" ", ["logo"]), # blank id contributes nothing
_Drive("LOGOS_A", ["logo"]),
_Drive("LOGOS_B", ["logo"]), # later claimer must not win
]
)
)
assert drive_ids == ["LOGOS_A", "LOGOS_B"]
assert twin_of("logo") == "LOGOS_A"


# --- season formatting is not drift (Season 1 == Season 01, Specials, year) ---


Expand Down