diff --git a/app/connectors/direct_scrapers/__init__.py b/app/connectors/direct_scrapers/__init__.py
index 0b2ba72..9834329 100644
--- a/app/connectors/direct_scrapers/__init__.py
+++ b/app/connectors/direct_scrapers/__init__.py
@@ -189,6 +189,15 @@ ALL_BROWSE_SCRAPERS: list[type[BaseBrowseScraper]] = [
# na obsadę przepuszcza 62/115, a z nich 97% performerów ma ref tpdb/stashdb.
# Pilot: seen 62, new 25, updated 37 — 60% dopina się do scen, które już mamy.
KPorner8Scraper,
+ # SexuScraper (`sexu.py`) — ZBUDOWANY 2026-07-27, ale NIE ingestujemy. Metadane ma
+ # najlepsze z całej serii (studio 97%, 96% performerów z refem tpdb/stashdb, dokładna
+ # długość, świeżość w godzinach), tylko jednostka treści jest zła: to **klipy 5-10
+ # minut**, nie sceny. Mediana 571 s na stronie 30 i 459 s na 200, ZERO filmów ≥20 min
+ # w całym katalogu. Pilot na 240 scenach: 1 (jeden) canonical match, bo długość i
+ # tytuł, czyli sygnały resolvera, są przepisane pod tube. Przy ~340 uploadach dziennie
+ # to ~10k nierozwiązywalnych wierszy miesięcznie i klip obok pełnej sceny na stronie
+ # performera. Scraper i extractor zostają w repo — gdyby serwis kiedyś zaczął wrzucać
+ # pełne sceny, wystarczy dopisać klasę z powrotem tutaj.
# FullVideosPornScraper — dodany 2026-07-27 (ocena 3/5, ADD Z ZASTRZEŻENIAMI).
# = sextu.com pod nową marką (NIE klon fullmovies.xxx). BRAMKA NA OBSADĘ: 65-75%
# katalogu nie ma performera i weszłoby jako puste orphany, więc ingestujemy tylko
diff --git a/app/connectors/direct_scrapers/sexu.py b/app/connectors/direct_scrapers/sexu.py
new file mode 100644
index 0000000..77398db
--- /dev/null
+++ b/app/connectors/direct_scrapers/sexu.py
@@ -0,0 +1,188 @@
+"""sexu.com — browse scraper. Dodany 2026-07-27.
+
+Najlepsze metadane z całej tegorocznej serii kandydatów. Próbka 25 najnowszych scen:
+obsada 20/25 (80%), **kanał 25/25 i to realne studia** (New Sensations, Brutal X,
+Moms Bang Teens, 2 Girls 1 Camera), dokładna długość w sekundach 25/25, data 25/25.
+Świeżość: cała próbka miała 7-8 godzin. Listing daje 120 scen na stronę.
+
+Dlatego **nie ma tu bramki na obsadę** (w odróżnieniu od fullvideosporn czy 8kporner).
+Te 20% scen bez performera i tak niesie studio + dokładną długość + oryginalny tytuł,
+czyli komplet sygnałów, których resolver potrzebuje do canonical matcha. Bramka
+odrzuciłaby dobre dane.
+
+**Dwie pułapki w HTML, obie sprawdzone:**
+
+1. **Tagi trzeba wyciąć z bloku.** Linków `/tag/` jest na stronie 25, ale realnych
+ tagów sceny tylko 5 (reszta to sidebar „popularne wyszukiwania"). Bez zawężenia
+ do bloku „Tags:" wpisalibyśmy każdej scenie te same 20 śmieci.
+2. **W bloku tagów siedzą też linki `/search?q=`** — to fraz-y pod SEO („pleases",
+ „hole", „indian milf"), nie tagi. Bierzemy wyłącznie `/tag/`.
+
+**Studio.** Kanał to zwykle studio, ale przy uploadach amatorskich bywa nazwany po
+performerze (`/channel/priya+rai` przy scenie z Priya Rai). Taki kanał odrzucamy,
+bo performer w tabeli studios to zanieczyszczenie, nie dana.
+
+Data jest względna („7 hours ago", „yesterday"), więc liczymy ją wstecz od teraz.
+To i tak data uploadu na tube, nie premiery studia.
+
+Playback: `POST /api/video-info` (extractor `sexu`) — wymaga Bright Data, bo
+Cloudflare blokuje POST z VPS. Sam scrape listingu i detali idzie bez proxy.
+"""
+from __future__ import annotations
+
+import html as html_mod
+import logging
+import re
+from datetime import UTC, datetime, timedelta
+
+from app.connectors.base import (
+ RawPerformer,
+ RawPlaybackSource,
+ RawScene,
+ RawStudio,
+ RawTag,
+)
+from app.connectors.direct_scrapers._browse_base import BaseBrowseScraper
+from app.normalize.text import slugify
+
+log = logging.getLogger(__name__)
+
+_BASE = "https://sexu.com"
+
+_SCENE_HREF_RE = re.compile(r'href="(/\d+/)"')
+_TITLE_RE = re.compile(r"
`.
+# Linki sceny są między tytułem a `_container` (tam zaczyna się formularz „Suggest…").
+_BLOCK_END_RE = re.compile(r"_container|player-tags__title")
+
+
+def _block(detail_html: str, label: str) -> str:
+ """Fragment HTML należący do bloku `