diff --git a/app/api/playback.py b/app/api/playback.py index 1d3cb08..948e9ba 100644 --- a/app/api/playback.py +++ b/app/api/playback.py @@ -113,6 +113,11 @@ _IP_BOUND_CDN_RE = re.compile( ) +#: Hostery filmow rozwiazywane wylacznie na telefonie (mobile/src/lib/packerHoster.ts). +#: sextapes.to (xtapes.la, 2026-10-15): HLS na cdn-centaurus zwiazany z IP rozwiazujacego. +_PHONE_RESOLVE_HOSTER_RE = re.compile(r"//(?:[a-z0-9-]+\.)*sextapes\.to/", re.IGNORECASE) + + class StreamLink(BaseModel): """Pojedynczy variant stream URL (różne quality / kontener). @@ -223,7 +228,13 @@ def resolve_movie_playback( target = pb.embed_url or pb.page_url stream: str | None = None try: - stream = extract_stream_from_hoster(target, referer=referer) + # Hostery rozwiazywane na telefonie: link z VPS bylby zwiazany z IP serwera (403 na + # telefonie, potem cale wideo przez proxy). Telefon dostaje sam embed. + stream = ( + None + if _PHONE_RESOLVE_HOSTER_RE.search(target) + else extract_stream_from_hoster(target, referer=referer) + ) except HosterDead as e: # Hoster wprost mówi "video deleted", oznacz dead, NIE proponuj # embed fallback (mobile ExoPlayer dostałby 404 HTML page i diff --git a/app/connectors/__init__.py b/app/connectors/__init__.py index 55799eb..cd68a3e 100644 --- a/app/connectors/__init__.py +++ b/app/connectors/__init__.py @@ -39,6 +39,7 @@ def get_movie_connectors() -> list[tuple[str, type]]: ) from app.connectors.freeomovie import FreeoMovieConnector from app.connectors.paradisehill import ParadisehillConnector + from app.connectors.xtapes import XtapesConnector from app.connectors.yourdailypornmovies import YourDailyPornMoviesConnector # Kolejność ingestu: paradisehill FIRST (canonical primary, mirrory się do @@ -76,4 +77,7 @@ def get_movie_connectors() -> list[tuple[str, type]]: # id produktu Adult Empire. Bez długości. Playback: voe + mixdrop (oba mają # gotowe moduły hosterów), vidara.to nieznana, leci jako zwykły embed. ("yourdailypornmovies", YourDailyPornMoviesConnector), + # xtapes.la (WordPress, REST API), kategoria Full Movies: 3-5 filmow dziennie, + # czesto w dniu premiery. Ocena 2026-10-15, szczegoly w app/connectors/xtapes.py. + ("xtapes", XtapesConnector), ] diff --git a/app/connectors/xtapes.py b/app/connectors/xtapes.py new file mode 100644 index 0000000..17c2385 --- /dev/null +++ b/app/connectors/xtapes.py @@ -0,0 +1,225 @@ +"""xtapes.la, konektor filmow (kategoria "Full Movies"). Dodany 2026-10-15. + +WordPress z otwartym REST API, wiec liste bierzemy z `/wp-json/wp/v2/posts` z filtrem +kategorii, a nie z HTML-a. Ocena przed dodaniem (2026-10-15): 1846 filmow, 3 do 5 nowych +dziennie, czesto w dniu premiery; z 30 najnowszych 10 nie mielismy wcale, reszte z +yourdailypornmovies/paradisehill/mangoporn (xtapes dokleja im kolejny link). + +Metadane z REST (`_embed=wp:term`): +- tytul "TYTUL (RRRR) - Full Movie" -> tytul i rok, +- studio to kategoria w nawiasie kwadratowym ("[EvilAngel]", "[Brazzers Movies]"), +- pozostale kategorie to gatunki (bez "Full Movies"), +- obsada to tagi (`post_tag`), +- opis z `content` (czesto pusty). +Ze strony filmu: odtwarzacze (iframe), dlugosc (`itemprop="duration"`), okladka (og:image). + +Odtwarzacze: sextapes.to (P.A.C.K.E.R. + JWPlayer, HLS typu StreamWish) i maxcloud.fit. +maxcloud to posrednik (lista serwerow w base64, za nia kolejny serwis z blokada devtools), +wiec go pomijamy. Link sextapes jest zwiazany z IP rozwiazujacego (z VPS 403 z innego IP), +dlatego rozwiazuje go telefon (packerHoster.ts), a backend go nie ekstrahuje (playback.py). + +Dostep: VPS dostaje od Cloudflare 403 na xtapes.la (REST i strony), przez Bright Data 200. +Proxy tylko do HTML/JSON, nie do wideo. sextapes.to z VPS odpowiada bez proxy. +""" +from __future__ import annotations + +import html as html_mod +import json +import logging +import re +from collections.abc import Iterator +from datetime import datetime +from urllib.parse import urlparse + +from app.config import get_settings +from app.connectors.base import ( + BaseMovieConnector, + RawMovie, + RawPerformer, + RawPlaybackSource, + RawStudio, + RawTag, +) +from app.extractors import browser_get +from app.models.source import SourceKind +from app.normalize.text import slugify + +log = logging.getLogger(__name__) + +_BASE = "https://xtapes.la" +#: Id kategorii "Full Movies" (sprawdzone 2026-10-15; slugi kategorii sa zaciemnione liczbami). +_MOVIES_CATEGORY = 1766 +_PER_PAGE = 30 + +_TITLE_RE = re.compile( + r"^(?P