Files
google-labs-jules[bot] af4c9461e8 Refactor legacy codebase into AniScrap Django platform (#45)
- Initialized 'aniscrap_core' project.
- Implemented 'users', 'content', 'billing', 'scraper_module', 'core' apps.
- Added models for Anime, Episode, VideoFile (HLS/Encrypted), User, Wallet, etc.
- Implemented Celery tasks for Video Encoding (H.265/HLS) and Revenue Distribution.
- Integrated Shopier mock payment flow.
- Configured Unfold Admin Dashboard.
- Refactored Jikan scraper client.
- Added 'init_aniscrap' management command.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-01-19 09:52:12 +00:00

203 lines
6.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""AnimeciX kaynağı (minimal Python port)
Bu modül, AnimeciX API uçlarından arama ve bölüm/izleme verilerini çeker.
Mevcut `objects.Anime/Bolum/Video` yapısına dokunmamak için, yalnızca
harici arama/episode/watch listesi sağlar; indirme/oynatma yine yt-dlp/mpv ile.
Cloudflare koruması için cf_bypass modülü entegre edilmiştir.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import List, Dict, Any, Optional, Tuple
import json
import re
from urllib.parse import urlparse, parse_qs, quote, urlsplit, urlunsplit
import urllib.request
# Cloudflare bypass entegrasyonu
try:
from .common.cf_bypass import CFSession, CFBypassError
HAS_CF_BYPASS = True
except ImportError:
HAS_CF_BYPASS = False
BASE_URL = "https://animecix.tv/"
ALT_URL = "https://mangacix.net/"
HEADERS = {"Accept": "application/json", "User-Agent": "Mozilla/5.0"}
VIDEO_PLAYERS = ["tau-video.xyz", "sibnet"]
# Global CF session (lazy-load)
_cf_session: Optional[CFSession] = None
def _get_cf_session() -> Optional[CFSession]:
"""CF session'ı lazy-load et."""
global _cf_session
if _cf_session is None and HAS_CF_BYPASS:
_cf_session = CFSession(impersonate="chrome110", timeout=15, max_retries=3)
return _cf_session
def _http_get(url: str, timeout: int = 10) -> bytes:
"""HTTP GET isteği - önce CF bypass, sonra fallback urllib."""
# Non-ASCII pathleri ASCII'ye uygun hale getirmek için yüzde-encode et
sp = urlsplit(url)
safe_path = quote(sp.path, safe="/:%@")
safe_url = urlunsplit((sp.scheme, sp.netloc, safe_path, sp.query, sp.fragment))
# Önce CF bypass ile dene
cf_session = _get_cf_session()
if cf_session is not None:
try:
resp = cf_session.get(safe_url, headers=HEADERS)
if resp.status_code == 200:
return resp.content
except (CFBypassError, Exception) as e:
print(f"[AnimeCix] CF bypass başarısız, fallback kullanılıyor: {e}")
# Fallback: Normal urllib
req = urllib.request.Request(safe_url, headers=HEADERS)
with urllib.request.urlopen(req, timeout=timeout) as resp:
return resp.read()
def search_animecix(query: str, timeout: int = 8) -> List[Tuple[str, str]]:
# Boşluk -> '-' ve non-ASCII karakterleri encode et
q = (query or "").strip().replace(" ", "-")
q_enc = quote(q, safe="-")
url = f"{BASE_URL}secure/search/{q_enc}?type=&limit=20"
data = json.loads(_http_get(url, timeout=timeout))
results = []
res = data.get("results") or []
for item in res:
name = item.get("name")
_id = item.get("id")
if name is None or _id is None:
continue
results.append((str(_id), str(name)))
return results
def _seasons_for_title(title_id: int) -> List[int]:
# title_id'yi güvenli şekilde int'e çevir
try:
safe_id = int(title_id)
except (ValueError, TypeError):
safe_id = hash(str(title_id)) % 1000000 if title_id else 0
url = f"{ALT_URL}secure/related-videos?episode=1&season=1&titleId={safe_id}&videoId=637113"
data = json.loads(_http_get(url))
videos = data.get("videos") or []
if not videos:
return []
title = (videos[0] or {}).get("title") or {}
seasons = title.get("seasons") or []
return list(range(len(seasons)))
def _episodes_for_title(title_id: int) -> List[Dict[str, Any]]:
# title_id'yi güvenli şekilde int'e çevir
try:
safe_id = int(title_id)
except (ValueError, TypeError):
safe_id = hash(str(title_id)) % 1000000 if title_id else 0
episodes: List[Dict[str, Any]] = []
seen = set()
for sidx in _seasons_for_title(safe_id):
url = (
f"{ALT_URL}secure/related-videos?"
f"episode=1&season={sidx+1}&titleId={safe_id}&videoId=637113"
)
data = json.loads(_http_get(url))
for v in data.get("videos", []):
name = v.get("name")
ep_url = v.get("url")
if not name or not ep_url:
continue
if name in seen:
continue
episodes.append(
{"name": name, "url": ep_url, "season_num": v.get("season_num")}
)
seen.add(name)
return episodes
def _video_streams(embed_path: str) -> List[Dict[str, str]]:
# BASE_URL + embed path'e gidip yönlendirilmiş URL'den player id/vid al
import http.client
import ssl
# Embed path non-ASCII içerebilir; güvenle encode et
full = f"{BASE_URL}{quote(embed_path, safe='/:?=&')}"
# Basit urllib ile final URL
resp = urllib.request.urlopen(urllib.request.Request(full, headers=HEADERS))
final_url = resp.geturl()
p = urlparse(final_url)
parts = p.path.strip("/").split("/")
if len(parts) < 2:
return []
embed_id = parts[1] if parts[0] == "embed" else parts[0]
qs = parse_qs(p.query)
vid = (qs.get("vid") or [None])[0]
if not embed_id or not vid:
return []
api = f"https://{VIDEO_PLAYERS[0]}/api/video/{embed_id}?vid={vid}"
data = json.loads(_http_get(api))
out: List[Dict[str, str]] = []
for u in data.get("urls", []):
label = u.get("label")
url = u.get("url")
if label and url:
out.append({"label": label, "url": url})
return out
@dataclass
class CixEpisode:
title: str
url: str
@dataclass
class CixAnime:
"""AnimeciX başlığı.
Not: Bu sınıf, yalnızca isim ve bölümleri sağlar. Oynatma/indirme için
mevcut Video/Bolum akışı kullanılmaya devam edilir.
"""
id: str # ID artık string olabilir
title: str
@property
def episodes(self) -> List[CixEpisode]:
# ID'yi güvenli şekilde int'e çevir
try:
title_id = int(self.id)
except (ValueError, TypeError):
# String ID ise hash değeri al
title_id = (
hash(self.id) % 1000000 if isinstance(self.id, str) and self.id else 0
)
eps = _episodes_for_title(title_id)
out: List[CixEpisode] = []
for i, e in enumerate(eps):
out.append(
CixEpisode(
title=e.get("name") or f"Bölüm {i+1}", url=e.get("url") or ""
)
)
return out
# Alias for compatibility with video_service
get_episode_streams = _video_streams