fix(ingest,proxy): pusta strona to nie koniec katalogu + miniaturki blokowane po IP
naughtymachinima (zgloszenie 83eb300d, "tylko 148 scen"): deep-crawl uznal katalog za wyczerpany na stronie 8, bo trafil na pusta strone. Listing ma 1770 stron po 30 filmow, a ponowne wejscie na strone 9 oddalo 13 scen i ZERO bledow pobrania, czyli tamto zero bylo chwilowym dlawieniem. Pusta strona bywa tez calkiem legalna, bo bramka odrzuca klipy < 180 s, a to 2/3 tego katalogu. Konczymy wiec dopiero po trzech pustych stronach pod rzad. Kursor odblokowany, kontrolny run przeszedl strony 9-14: 49 scen, 7 nowych, bez oznaczenia exhausted. fullvideosporn (zgloszenie f4995691, "brak miniaturek"): adresy w bazie sa poprawne, ale tn.sextu.com odrzuca nasz adres serwerowni: 429 przy kazdej probie, takze po przerwie, a przez residential proxy 200 i 26 kB. Proxy obrazkow po odmowie (401/403/429) ponawia teraz raz przez to proxy. Obrazek ma kilkadziesiat kB i klient cachuje go dobe, wiec to nie jest proxowanie wideo. Sprawdzone: 6/6 miniaturek laduje sie (15-116 kB), wczesniej 0/6. Uwaga na przyszlosc: helper wladowal sie przy pierwszym podejsciu MIEDZY dekorator trasy a proxy_image, wiec @router.get udekorowal jego, a /proxy/img/ oddawalo URL proxy z loginem i haslem jako JSON. Naprawione, w docstringu ostrzezenie. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
fa7fb668b6
commit
e26c78df24
2 changed files with 63 additions and 3 deletions
|
|
@ -315,6 +315,19 @@ def sign_url(
|
|||
return {"token": make_token(url, referer), "expires_in": TOKEN_TTL_SEC}
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=1)
|
||||
def _fallback_proxy_url() -> str | None:
|
||||
"""Residential proxy, uzywany TYLKO gdy CDN odrzucil nasz adres IP.
|
||||
|
||||
NIE wstawiaj niczego miedzy dekorator trasy a `proxy_image`: przy pierwszym podejsciu
|
||||
ta funkcja wladowala sie wlasnie tam i `@router.get` udekorowal JA, wiec /proxy/img/
|
||||
oddawalo URL proxy razem z loginem i haslem jako JSON.
|
||||
"""
|
||||
from app.config import get_settings
|
||||
|
||||
return getattr(get_settings(), "brightdata_proxy_url", None) or None
|
||||
|
||||
|
||||
@router.get("/img/{token}/{_basename:path}")
|
||||
async def proxy_image(
|
||||
token: str,
|
||||
|
|
@ -365,6 +378,26 @@ async def proxy_image(
|
|||
r = r2
|
||||
except Exception as e:
|
||||
log.info("img proxy retry z wlasnym originem padl %s: %s", target, e)
|
||||
if r.status_code in (401, 403, 429):
|
||||
# Ostatnia deska ratunku: CDN blokuje NASZ adres IP, nie zapytanie. Miniaturki
|
||||
# fullvideosporn (tn.sextu.com) oddaja z serwerowni 429 przy kazdej probie
|
||||
# (3/3, tez po przerwie), a przez residential proxy 200 i 26 kB. Stad puste
|
||||
# kafelki mimo poprawnych adresow w bazie (zgloszenie f4995691). Obrazek ma
|
||||
# kilkadziesiat kB i klient cachuje go na dobe, wiec to nie jest ten sam
|
||||
# przypadek co proxowanie wideo. Sciagamy tylko wtedy, gdy host nas odrzucil.
|
||||
proxy_url = _fallback_proxy_url()
|
||||
if proxy_url:
|
||||
try:
|
||||
async with httpx.AsyncClient(
|
||||
timeout=timeout, follow_redirects=True, proxy=proxy_url
|
||||
) as pclient:
|
||||
r3 = await pclient.get(target, headers=headers)
|
||||
if r3.status_code < 400:
|
||||
r = r3
|
||||
else:
|
||||
log.info("img proxy: proxy tez oddal %d dla %s", r3.status_code, target)
|
||||
except Exception as e:
|
||||
log.info("img proxy: proba przez proxy padla %s: %s", target, e)
|
||||
if r.status_code >= 400:
|
||||
# Upstream 4xx/5xx dla thumba, degraded zamiast raise (placeholder w mobile).
|
||||
# GOON-5 (Cloudflare 523) i GOON-D, bezsensowny noise w Sentry, lepiej
|
||||
|
|
|
|||
|
|
@ -108,6 +108,12 @@ def _pick_target(state: dict, targets: list[str]) -> str | None:
|
|||
return live[0]
|
||||
|
||||
|
||||
#: Ile pustych stron POD RZAD uznajemy za koniec katalogu. Jedna nie wystarcza, patrz
|
||||
#: komentarz w petli: serwis moze chwilowo dlawic, a strona moze byc w calosci odrzucona
|
||||
#: przez bramki jakosci.
|
||||
_EMPTY_PAGES_BEFORE_END = 3
|
||||
|
||||
|
||||
def run_deep_crawl(*, pages_per_run: int = 60, sitetags: list[str] | None = None) -> dict:
|
||||
"""Jeden run: wybierz tube, crawl kolejne `pages_per_run` stron od kursora, ingest.
|
||||
Zwraca podsumowanie (sitetag, zakres stron, counters, exhausted)."""
|
||||
|
|
@ -142,6 +148,7 @@ def run_deep_crawl(*, pages_per_run: int = 60, sitetags: list[str] | None = None
|
|||
t0 = time.time()
|
||||
last_done = start - 1
|
||||
exhausted = False
|
||||
empty_streak = 0
|
||||
budget_hit = False
|
||||
|
||||
st = state.setdefault(sitetag, {})
|
||||
|
|
@ -168,10 +175,30 @@ def run_deep_crawl(*, pages_per_run: int = 60, sitetags: list[str] | None = None
|
|||
# transient fetch-fail listingu, NIE awansuj kursora, następny run powtórzy
|
||||
break
|
||||
if not scenes:
|
||||
log.info("deep-crawl %s: empty page %d → catalog end (exhausted)", sitetag, page)
|
||||
exhausted = True
|
||||
# Pusta strona to NIE dowod konca katalogu. Dwa powody, oba realne:
|
||||
# 1. Serwis chwilowo dlawi ruch, wiec wszystkie fetche szczegolow z tej
|
||||
# strony padaja i zostaje pusta lista.
|
||||
# 2. Na stronie faktycznie nic nie przeszlo naszych bramek (np.
|
||||
# naughtymachinima odrzuca klipy < 180 s, a to 2/3 jego katalogu).
|
||||
# Naughtymachinima zostalo tak oznaczone jako `exhausted` na stronie 8,
|
||||
# mimo ze listing ma 1770 stron po 30 filmow, a ponowne wejscie na
|
||||
# strone 9 oddalo 13 scen i zero bledow pobrania. Zostalo 158 scen
|
||||
# zamiast tysiecy (zgloszenie 83eb300d). Dlatego konczymy dopiero po
|
||||
# kilku pustych stronach POD RZAD.
|
||||
empty_streak += 1
|
||||
last_done = page
|
||||
break
|
||||
_persist()
|
||||
if empty_streak >= _EMPTY_PAGES_BEFORE_END:
|
||||
log.info(
|
||||
"deep-crawl %s: %d pustych stron pod rzad (do %d) → koniec katalogu",
|
||||
sitetag, empty_streak, page,
|
||||
)
|
||||
exhausted = True
|
||||
break
|
||||
log.info("deep-crawl %s: pusta strona %d (%d/%d), lece dalej",
|
||||
sitetag, page, empty_streak, _EMPTY_PAGES_BEFORE_END)
|
||||
continue
|
||||
empty_streak = 0
|
||||
page_is_backfill = (page > _LATEST_PAGE_THRESHOLD) and not swept_once
|
||||
for raw in scenes:
|
||||
counters["seen"] += 1
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue