From d4c4b79e92e5db9e07c384d95c6647d35007aa99 Mon Sep 17 00:00:00 2001 From: jtrzupek Date: Tue, 2 Jun 2026 21:33:05 +0200 Subject: [PATCH] fix(kvs): cap get_file timeout + early-break on dead scenes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 6ec1960e: yespornvip "resolving forever". yesporn.vip moved to a cdn4/remote_control.php CDN (still portable cross-IP — verified 206 from a residential IP, so backend resolve stays correct). But when a video is removed from the CDN the page still exists and each get_file 302-follow STALLS to the full timeout. With the resolve timeout (60s) applied per quality variant, a dead scene hung 3x60 = 180s and returned nothing -> the mobile resolve spinner never ended. Fix: a dedicated low get_file timeout (10s, separate from the page-fetch timeout) and an early-break once 2 variants fail with no result so far (the scene is dead on the CDN — no point waiting for the third). Dead scene now resolves to None in ~20s instead of 180s; a live scene is unaffected (~0.8s, 3 sources). Applies to all KVS tubes (yespornvip + pornditt). Co-Authored-By: Claude Opus 4.8 (1M context) --- app/extractors/tubes/_kvs.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/extractors/tubes/_kvs.py b/app/extractors/tubes/_kvs.py index 0bc7270..3a5ed94 100644 --- a/app/extractors/tubes/_kvs.py +++ b/app/extractors/tubes/_kvs.py @@ -40,6 +40,13 @@ _LICENSE_RE = re.compile(r"license_code\s*:\s*[\"'](\$[^\"']+)[\"']", re.IGNOREC _HASH_LENGTH = 32 +# get_file 302-follow timeout. Zdrowy CDN odpowiada <1s; gdy wideo zostało usunięte +# z CDN (strona istnieje, ale get_file stalluje) request wisi do timeoutu. Trzymamy +# go NISKO i osobno od page-fetch timeoutu — bug 6ec1960e 2026-06-02: yesporn.vip +# przeszedł na cdn4/remote_control.php i dla martwych scen wszystkie 3 jakości +# wisiały po 60s = 180s → mobile "resolving w nieskończoność". +_GETFILE_TIMEOUT = 10.0 + def _license_token(license_code: str) -> list[int]: license_code = license_code.replace("$", "") @@ -134,16 +141,25 @@ def resolve_kvs(page_url: str, *, base_url: str, timeout: float = 60.0) -> list[ for m in _TEXT_RE.finditer(html): quality_by_var[m.group(1).lower()] = m.group(2).strip() + # get_file timeout NISKI (osobny od page-fetch) + early-break: gdy 2 pierwsze + # jakości nie rozwiążą się (przy 0 dotychczasowych wyników) scena jest martwa na + # CDN — nie ma sensu czekać na trzecią (oszczędza kolejne _GETFILE_TIMEOUT). + gf_timeout = min(timeout, _GETFILE_TIMEOUT) seen_dec: set[str] = set() result: list[StreamSource] = [] + fails = 0 for m in _URL_RE.finditer(html): var_name = m.group(1).lower() decoded = real_url(m.group(2), license_code) if decoded in seen_dec: continue seen_dec.add(decoded) - final = _resolve_get_file(session, base_url, decoded, timeout) + final = _resolve_get_file(session, base_url, decoded, gf_timeout) if not final: + fails += 1 + if fails >= 2 and not result: + log.info("kvs: %d get_file fails, 0 results — martwa scena? %s", fails, page_url) + break continue result.append( StreamSource(