Nouvelle version réécrite de zéro : recherche multi-sources (Vostfree, French-Manga), extraction 2 niveaux, proxy vidéo intégré, streaming/téléchargement HLS, métadonnées Kitsu, bibliothèque locale, comptes JWT + administration, découverte fusionnée, indexeur Torznab (Sonarr/Prowlarr).
70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
"""Extracteur Sibnet (video.sibnet.ru) — page shell/watch → mp4 direct.
|
|
|
|
La page embed `shell.php?videoid=N` (ou la page publique `/videoN`) contient
|
|
une config jwplayer du type `player.src([{src: "/v/<hash>/<id>.mp4"}])`.
|
|
`shell.php` répond 403 depuis certains réseaux : on retombe alors sur la page
|
|
publique de la vidéo, qui expose la même config.
|
|
"""
|
|
|
|
import logging
|
|
import re
|
|
from urllib.parse import urljoin
|
|
|
|
from app.scrapers.base import HosterExtractor, ScrapeError, VideoLink, register_hoster
|
|
from app.scrapers.http import fetch
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_BASE = "https://video.sibnet.ru"
|
|
_VIDEO_ID_RE = re.compile(r"(?:videoid=|/video)(\d+)")
|
|
_MP4_PATTERNS = (
|
|
re.compile(r'player\.src\(\[\{src:\s*"(?P<url>/v/[^"]+?\.mp4)"'),
|
|
re.compile(r'["\'](?P<url>/v/[0-9a-f]{32}/\d+\.mp4)["\']'),
|
|
re.compile(r'["\'](?P<url>https?://[^"\']*?/v/[^"\']+?\.mp4)["\']'),
|
|
)
|
|
|
|
|
|
@register_hoster
|
|
class SibnetExtractor(HosterExtractor):
|
|
name = "sibnet"
|
|
domains = ("video.sibnet.ru",)
|
|
|
|
async def extract(self, embed_url: str) -> VideoLink:
|
|
match = _VIDEO_ID_RE.search(embed_url)
|
|
if not match:
|
|
raise ScrapeError(f"sibnet : identifiant vidéo introuvable dans {embed_url}")
|
|
video_id = match.group(1)
|
|
watch_url = f"{_BASE}/video{video_id}"
|
|
|
|
pages: list[tuple[str, str]] = []
|
|
try:
|
|
pages.append(("embed", await fetch(embed_url, retries=0)))
|
|
except ScrapeError as exc:
|
|
logger.info(
|
|
"sibnet : page embed %s inaccessible (%s), essai page publique", embed_url, exc
|
|
)
|
|
try:
|
|
pages.append(("watch", await fetch(watch_url, retries=1)))
|
|
except ScrapeError as exc:
|
|
logger.error("sibnet : page publique %s inaccessible : %s", watch_url, exc)
|
|
|
|
for source, html in pages:
|
|
url = self._find_mp4(html, source)
|
|
if url:
|
|
return VideoLink(
|
|
url=url,
|
|
hoster=self.name,
|
|
headers={"Referer": watch_url},
|
|
is_hls=False,
|
|
)
|
|
raise ScrapeError(f"sibnet : URL mp4 introuvable pour la vidéo {video_id}")
|
|
|
|
def _find_mp4(self, html: str, source: str) -> str | None:
|
|
for pattern in _MP4_PATTERNS:
|
|
match = pattern.search(html)
|
|
if match:
|
|
url = urljoin(_BASE + "/", match.group("url"))
|
|
logger.debug("sibnet : mp4 trouvé via %s → %s", source, url)
|
|
return url
|
|
return None
|