fix(api): de-prioritize rotting sxyprn/trafficdeposit thumbnails

sxyprn thumbnails are time-signed on trafficdeposit CDN and ROT — the signed asset
404s after ~weeks and can't be re-signed/refreshed server-side (bug 2026-06-10,
~15k sxyprn-only scenes showed broken thumbs). In the light-list slim-thumbnail pick,
prefer a thumbnail from any non-trafficdeposit source; fall back to sxyprn only when
it's the scene's sole thumbnail (recent ones still load; dead ones now render a clean
placeholder client-side instead of a broken image).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jtrzupek 2026-06-10 10:11:10 +02:00
parent 200db33d78
commit adbdce1c75

View file

@ -442,6 +442,13 @@ def get_scene(
return _build_scene_out(session, scene, device_id=device_id)
def _is_rotting_thumb(url: str) -> bool:
"""sxyprn/trafficdeposit miniaturki są czasowo podpisane i rotują (asset 404 po
~tygodniach, nie odświeżalne server-side; bug 2026-06-10). De-prioritize je w wyborze
slim-thumbnaila używamy tylko gdy scena nie ma żadnej innej miniaturki."""
return "trafficdeposit.com" in url
def _needs_proxy(url: str) -> bool:
"""Wszystkie thumbnaile z playback_sources są proxowane przez backend.
Większość CDN-ów porn-tube'ów wymaga Refera (hqporner, mypornerleak/58img,
@ -557,14 +564,25 @@ def _build_scenes_out_batch(
)
.order_by(PlaybackSource.origin.asc())
).all()
# Pierwsza miniaturka + pierwszy animated per scena (1 slim wpis).
# Pierwsza miniaturka + pierwszy animated per scena (1 slim wpis). De-prioritize
# sxyprn/trafficdeposit thumbnaile — są podpisane czasowo i ROTUJĄ (asset 404 po
# ~tygodniach, nie da się re-signować; bug 2026-06-10). Wolimy miniaturkę z innego
# źródła gdy istnieje; sxyprn bierzemy tylko gdy nic innego nie ma (świeże jeszcze
# działają, martwe → mobile pokazuje placeholder zamiast broken-image).
thumb_by_scene: dict = {}
thumb_fallback: dict = {}
anim_by_scene: dict = {}
for sid, thumb, anim, page_url in pb_light:
if sid not in thumb_by_scene and thumb:
thumb_by_scene[sid] = (thumb, page_url)
if thumb:
if _is_rotting_thumb(thumb):
thumb_fallback.setdefault(sid, (thumb, page_url))
elif sid not in thumb_by_scene:
thumb_by_scene[sid] = (thumb, page_url)
if sid not in anim_by_scene and anim:
anim_by_scene[sid] = (anim, page_url)
# Uzupełnij scenami które mają TYLKO rotting thumbnail (sxyprn-only).
for sid, val in thumb_fallback.items():
thumb_by_scene.setdefault(sid, val)
for sid in scene_ids:
t = thumb_by_scene.get(sid)
a = anim_by_scene.get(sid)