diff --git a/app/extractors/tubes/galaxyporn.py b/app/extractors/tubes/galaxyporn.py index a471efc..b886e5e 100644 --- a/app/extractors/tubes/galaxyporn.py +++ b/app/extractors/tubes/galaxyporn.py @@ -15,7 +15,7 @@ import logging import re from urllib.parse import urlparse -from app.extractors._fetch import fetch_tube_html +from app.extractors._fetch import browser_get, fetch_tube_html from app.extractors._models import StreamSource from app.extractors.hosters import seekplayer_engine @@ -31,7 +31,20 @@ _NEEDS_WEBVIEW_RE = re.compile(r"vikingfile\.", re.IGNORECASE) _VIKINGFILE_RE = re.compile(r"https?://vikingfile\.com/f/[A-Za-z0-9]+", re.IGNORECASE) -def _vikingfile_source(html_text: str, page_url: str) -> list[StreamSource] | None: +def _follow_redirect(url: str, *, timeout: float) -> str: + """Adres po przekierowaniach; przy bledzie oddaje wejsciowy (lepszy niz nic).""" + try: + res = browser_get(url, timeout=min(timeout, 20.0), follow_redirects=True) + final = str(getattr(res, "url", "") or "") + return final or url + except Exception as e: + log.info("galaxyporn: nie rozwiazalem przekierowania %s: %s", url, e) + return url + + +def _vikingfile_source( + html_text: str, page_url: str, *, timeout: float = 30.0 +) -> list[StreamSource] | None: """Link do vikingfile z DOWOLNEGO miejsca strony, nie tylko z iframe'a. Na starszych stronach vikingfile siedzi w zwyklym ``, a iframe wskazuje @@ -40,9 +53,18 @@ def _vikingfile_source(html_text: str, page_url: str) -> list[StreamSource] | No mv = _VIKINGFILE_RE.search(html_text) if not mv: return None + # WAZNE: vikingfile.com oddaje 302 na INNA domene (2026-08-22: vik1ngfile.site). + # WebView w aplikacji blokuje przejscie top-frame na inny eTLD+1, zeby popundery nie + # przejmowaly ekranu, wiec to przekierowanie bylo odrzucane i user dostawal CZARNY + # EKRAN (zgloszenie 21d6f5af). Widac to bylo dopiero na emulatorze, bo z serwera + # httpx sledzi przekierowanie po cichu. Oddajemy wiec adres DOCELOWY, zeby aplikacja + # od razu otwierala wlasciwa domene i zadna zmiana domeny w ogole nie zachodzila. + # Domena zapasowa potrafi rotowac, dlatego rozwiazujemy ja za kazdym razem, a nie + # wpisujemy na sztywno. + link = _follow_redirect(mv.group(0), timeout=timeout) return [ StreamSource( - link=mv.group(0), + link=link, quality="hoster", type="hoster", referer=page_url, @@ -56,7 +78,7 @@ def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | Non m = _IFRAME_RE.search(html_text) if not m: log.info("galaxyporn: no iframe on %s", page_url) - return _vikingfile_source(html_text, page_url) + return _vikingfile_source(html_text, page_url, timeout=timeout) iframe_src = m.group(1).strip() if iframe_src.startswith("//"): iframe_src = "https:" + iframe_src @@ -91,7 +113,7 @@ def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | Non # oddac pustke. Bez tego wyjatek wychodzil na zewnatrz i ratunek z vikingfile # nigdy sie nie wykonywal. log.info("galaxyporn: seekplayer rzucil dla %s (%s), probuje vikingfile", iframe_src, e) - return _vikingfile_source(html_text, page_url) + return _vikingfile_source(html_text, page_url, timeout=timeout) if not sources: # Iframe bywa ZWIETRZALY. Serwis przeniosl pliki na vikingfile, ale w starszych # wpisach zostawil stary iframe (news.upns.pro, sport.seekplays.com, @@ -103,7 +125,7 @@ def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | Non # seekplayer PIERWSZY, bo resolwuje sie po naszej stronie i gra bez CAPTCHA, # a vikingfile to ratunek, ktory kosztuje usera przejscie Turnstile w WebView. log.info("galaxyporn: seekplayer padl dla %s, probuje vikingfile", iframe_src) - return _vikingfile_source(html_text, page_url) + return _vikingfile_source(html_text, page_url, timeout=timeout) # Referer MUSI być hostem PLAYERA (np. https://galaxy.4meplayer.pro/), NIE # galaxyporn.net, zweryfikowane: ten sam manifest z Refererem galaxyporn = 403, diff --git a/mobile/src/changelog.ts b/mobile/src/changelog.ts index 7baddc0..72180f1 100644 --- a/mobile/src/changelog.ts +++ b/mobile/src/changelog.ts @@ -20,7 +20,7 @@ export const CHANGELOG: ChangelogEntry[] = [ id: '2026-08-22c', date: 'August 2026', items: [ - 'Fixed the black screen on older galaxyporn scenes. The player was covering the host page for a fixed 15 seconds, so by the time anything appeared there was nothing left to see. Now the page shows the moment it needs you, and when the app can grab the video file itself it skips the page entirely and plays it in the normal player.', + 'Fixed the black screen on older galaxyporn scenes. The file host bounces you to a second domain, and the player was blocking that jump as if it were a pop-up, so nothing ever loaded. The app now opens the right address straight away, shows the confirm-you-are-human box as soon as it appears, and where it can grab the video file itself it skips the page and plays it in the normal player.', ], }, { diff --git a/mobile/src/screens/PlayerScreen.tsx b/mobile/src/screens/PlayerScreen.tsx index 2144436..4b118a2 100644 --- a/mobile/src/screens/PlayerScreen.tsx +++ b/mobile/src/screens/PlayerScreen.tsx @@ -1156,7 +1156,8 @@ const INJECTED_JS = ` if (window.__goonPatched) return; window.__goonPatched = true; - // -- 0. vikingfile: to STRONA PLIKU, nie goly odtwarzacz. Galaxyporn przeniosl tam + // -- 0. vikingfile: to STRONA PLIKU, nie goly odtwarzacz. UWAGA: serwis rotuje + // domena, vikingfile.com oddaje 302 na vik1ngfile.site, stad luzniejszy wzorzec. Galaxyporn przeniosl tam // swoj katalog, a my podajemy ja do WebView, bo wideo odslania sie dopiero po // recznym przejsciu zabezpieczenia Cloudflare. Bez tego bloku user widzi // naglowek serwisu, nazwe pliku i stopke, a wlasciwy widget bywa pod zgieciem, @@ -1166,7 +1167,7 @@ const INJECTED_JS = ` // obraz w calosci; dalej dziala zwykly scrape , wiec i tak przeskoczymy // na natywny odtwarzacz, gdy tylko pojawi sie adres pliku. try { - if (/(^|\\.)vikingfile\\.com$/i.test(location.hostname)) { + if (/vik[i1]ngfile\\./i.test(location.hostname)) { // UWAGA: NIE chowamy tu reszty strony. Pierwsza wersja robila dokladnie to i user // dostawal CZARNY EKRAN zamiast pustej strony, bo gdy widget zabezpieczenia sie nie // wyrenderuje, na czarnym tle nie zostaje juz nic. Gorsze niz stan wyjsciowy. Teraz