diff --git a/app/connectors/direct_scrapers/vjav.py b/app/connectors/direct_scrapers/vjav.py index 82f5f34..d58f7c3 100644 --- a/app/connectors/direct_scrapers/vjav.py +++ b/app/connectors/direct_scrapers/vjav.py @@ -31,6 +31,7 @@ from app.connectors.base import ( RawStudio, RawTag, ) +from app.config import get_settings from app.connectors.direct_scrapers._browse_base import BaseBrowseScraper from app.extractors import browser_get from app.normalize.text import slugify @@ -74,21 +75,45 @@ class VjavScraper(BaseBrowseScraper): super().__init__(*args, **kwargs) # Cache id-ów wideo (posortowane malejąco = newest first), per instancja/run. self._sorted_ids: list[int] | None = None + # vjav odciął IP VPS-a: 2026-08-03 KAŻDA ścieżka (sitemap, strona główna, + # /latest-updates/) oddawała 429 przy trzech próbach z rzędu, a przez proxy + # 200 od pierwszej. Źródło stało 12 dni. Bright Data jest tu więc warunkiem + # działania, nie optymalizacją — bez klucza scraper się wyłącza. + self._proxy = get_settings().brightdata_proxy_url + if not self._proxy: + log.warning("vjav: BRAK BRIGHTDATA_PROXY_URL — vjav blokuje IP VPS-a (429)") # ---- browse: sitemap id-walk ------------------------------------------------ def _load_ids(self) -> None: + """`self._sorted_ids = []` znaczy „katalog pusty", a to woła `crawl_page` do + zwrócenia [], czyli „koniec katalogu". Dlatego błąd pobrania NIE MOŻE tu + ustawiać []: przy 429 scraper raportował 0 scen i 0 błędów, więc przez 12 dni + wyglądał na zdrowy. Zostawiamy None = „nie wiadomo", co crawl_page tłumaczy na + transient-fail. Status sprawdzamy jawnie, bo `browser_get` nie rzuca na 4xx — + i to właśnie przez to treść błędu 429 była parsowana jako XML.""" try: - res = browser_get(_SITEMAP_INDEX, timeout=self._timeout) + res = browser_get(_SITEMAP_INDEX, timeout=self._timeout, proxy=self._proxy) + if getattr(res, "status_code", 200) >= 400: + log.warning("vjav: sitemap index HTTP %s — przerywam", res.status_code) + self._sorted_ids = None + return idx = res.text if hasattr(res, "text") else res except Exception as e: log.warning("vjav: sitemap index fetch fail: %s", e) - self._sorted_ids = [] + self._sorted_ids = None return maps = list(dict.fromkeys(_SITEMAP_RE.findall(idx))) + if not maps: + log.warning("vjav: sitemap index bez sitemap wideo (%d B) — przerywam", len(idx)) + self._sorted_ids = None + return ids: set[int] = set() for sm in maps: try: - res = browser_get(sm, timeout=self._timeout) + res = browser_get(sm, timeout=self._timeout, proxy=self._proxy) + if getattr(res, "status_code", 200) >= 400: + log.info("vjav: sitemap %s HTTP %s", sm, res.status_code) + continue body = res.text if hasattr(res, "text") else res except Exception as e: log.info("vjav: sitemap %s fetch fail: %s", sm, e) @@ -177,7 +202,14 @@ class VjavScraper(BaseBrowseScraper): out: list[RawScene] = [] for vid in chunk: try: - res = browser_get(self._meta_url(vid), timeout=self._timeout, headers={"Referer": _BASE + "/"}) + # Proxy także tutaj — API metadanych siedzi na tym samym hoście, więc + # obejmuje je ten sam 429 co sitemap. + res = browser_get( + self._meta_url(vid), + timeout=self._timeout, + headers={"Referer": _BASE + "/"}, + proxy=self._proxy, + ) body = res.text if hasattr(res, "text") else res v = json.loads(body).get("video") except Exception as e: