fix(vjav): powtorz pobranie manifestu z ciasteczkiem wyzwania (100% bledow -> gra)
vjav mial 587 bledow odtwarzania i ZERO udanych, przez caly czas, odkad zbieramy dla niego zdarzenia. Kazdy blad to ten sam komunikat odtwarzacza: "Source error Response code: 429". Dotyczylo to 58 779 zrodel, czyli tub nigdy ani razu nie zagral. Siec TXXX dlawi ruch z serwerowni: zamiast tresci oddaje 429 i mikroskopijna strone, ktora w JS ustawia jedno ciasteczko i przeladowuje sie. Wartosc stoi wprost w tej odpowiedzi, wiec wystarczy ja odczytac i powtorzyc zapytanie, bez wykonywania JS. Ekstraktor vjav robil dokladnie to od 2026-08-21, ale TYLKO przy `videofile.php`. Adres manifestu oddawany odtwarzaczowi tej sztuczki nie dostawal, a `/proxy/hls` pobiera go z VPS przy KAZDYM odtworzeniu. Stad komplet bledow. To dlatego bug byl niewidoczny z zewnatrz: `try_extract` konczyl sie sukcesem, wiec w kazdym pomiarze zrodla vjav wychodzily jako zywe (149 na 150), podczas gdy u userow nie dzialalo nic. "Zywe" znaczy "ekstraktor zwrocil adres", a nie "to gra". Sprawdzone na prodzie: przed poprawka /proxy/hls oddawal 429, po poprawce 200 i poprawny #EXTM3U z segmentami na vjav0.ahcdn.com. Ponowienie odpala sie wylacznie przy 429 z odpowiedzia ponizej 4 KB, wiec nie dotyka pozostalych hosterow. Osobno potwierdzone, ze blokada jest po adresie IP: ta sama strona z VPS oddaje 429, a przez proxy z innym IP 200 i 72 KB tresci.
This commit is contained in:
parent
4df82b0bbe
commit
b84ff5bdbc
1 changed files with 24 additions and 0 deletions
|
|
@ -695,6 +695,28 @@ async def _produce_manifest(producer: str, embed_url: str) -> str | None:
|
|||
return None
|
||||
|
||||
|
||||
_CHALLENGE_COOKIE_RE = re.compile(r"document\.cookie\s*=\s*'([^=]+)=([^;']+)")
|
||||
|
||||
|
||||
def _challenge_cookie(r: httpx.Response) -> str | None:
|
||||
"""Ciasteczko z odpowiedzi-wyzwania sieci TXXX (vjav i rodzina), albo None.
|
||||
|
||||
Serwis dlawi ruch z serwerowni: zamiast tresci oddaje 429 i mikroskopijna strone,
|
||||
ktora w JS ustawia jedno ciasteczko i przeladowuje sie. Wartosc stoi WPROST w tej
|
||||
odpowiedzi, wiec wystarczy ja odczytac i powtorzyc zapytanie, bez wykonywania JS.
|
||||
|
||||
Ekstraktor vjav robil to od 2026-08-21 przy `videofile.php`, ale adres manifestu
|
||||
oddawany odtwarzaczowi tej sztuczki nie dostawal. Skutek: `try_extract` konczyl sie
|
||||
sukcesem, wiec zrodlo wygladalo na zywe, a `/proxy/hls` przy kazdym odtworzeniu
|
||||
dostawal 429 i oddawal blad. Stad 587 bledow i ZERO udanych odtworzen na vjav w
|
||||
ciagu 14 dni, przy 58 779 zrodel.
|
||||
"""
|
||||
if r.status_code != 429 or len(r.content) > 4096:
|
||||
return None
|
||||
m = _CHALLENGE_COOKIE_RE.search(r.text)
|
||||
return f"{m.group(1)}={m.group(2)}" if m else None
|
||||
|
||||
|
||||
@router.get("/hls/{token}/{_basename:path}")
|
||||
async def proxy_hls_manifest(token: str, _basename: str) -> Response:
|
||||
"""Direct-HLS manifest passthrough dla time-bound (mobile_direct_ok) m3u8 hosterów.
|
||||
|
|
@ -732,6 +754,8 @@ async def proxy_hls_manifest(token: str, _basename: str) -> Response:
|
|||
async with httpx.AsyncClient(follow_redirects=True, timeout=20.0) as client:
|
||||
try:
|
||||
r = await client.get(target, headers=headers)
|
||||
if (ck := _challenge_cookie(r)) is not None:
|
||||
r = await client.get(target, headers=dict(headers, Cookie=ck))
|
||||
except httpx.HTTPError as e:
|
||||
log.info("proxy-hls fetch failed %s: %s", target, e)
|
||||
raise HTTPException(status_code=502, detail="manifest fetch failed") from e
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue