"""Extracteur SendVid (sendvid.com) — page embed → mp4 direct. La page embed expose soit une balise `` ( lecteur HTML5), soit une variable JS `video_source`. Extraction par regex, sans dépendance JS. """ import logging import re from app.scrapers.base import HosterExtractor, ScrapeError, VideoLink, register_hoster from app.scrapers.hosters._packer import unpack_packed_js from app.scrapers.http import fetch logger = logging.getLogger(__name__) _PATTERNS = ( re.compile(r']+src=["\'](?P[^"\']+)["\']'), re.compile(r'video_source\s*[:=]\s*["\'](?P[^"\']+)["\']'), re.compile(r'["\'](?Phttps?://[^"\']*?\.(?:mp4|m3u8)(?:\?[^"\']*)?)["\']'), ) @register_hoster class SendvidExtractor(HosterExtractor): name = "sendvid" domains = ("sendvid.com",) async def extract(self, embed_url: str) -> VideoLink: html = await fetch(embed_url) candidates: list[str] = [] for content in (html, unpack_packed_js(html) or ""): for pattern in _PATTERNS: for match in pattern.finditer(content): candidates.append(match.group("url")) url = self._pick(candidates) if not url: raise ScrapeError(f"sendvid : URL vidéo introuvable dans {embed_url}") return VideoLink( url=url, hoster=self.name, headers={"Referer": embed_url}, is_hls=".m3u8" in url, ) @staticmethod def _pick(candidates: list[str]) -> str | None: video = [u for u in candidates if re.search(r"\.(mp4|m3u8)(\?|$)", u)] for url in video: if ".mp4" in url: return url return video[0] if video else None