Merge pull request #171 from axtrat/provider/animeunity

This commit is contained in:
Benedict Xavier
2025-12-14 09:26:07 +03:00
committed by GitHub
4 changed files with 60 additions and 8 deletions
@@ -11,4 +11,7 @@ REPLACEMENT_WORDS = {"Season ": "", "Cour": "Part"}
# Server Specific
AVAILABLE_VIDEO_QUALITY = ["1080", "720", "480"]
VIDEO_INFO_REGEX = re.compile(r"window.video\s*=\s*(\{[^\}]*\})")
VIDEO_INFO_CLEAN_REGEX = re.compile(r'(?<!["\'])(\b\w+\b)(?=\s*:)')
DOWNLOAD_FILENAME_REGEX = re.compile(r"[?&]filename=([^&]+)")
QUALITY_REGEX = re.compile(r"/(\d{3,4}p)")
DOWNLOAD_URL_REGEX = re.compile(r"window.downloadUrl\s*=\s*'([^']*)'")
@@ -0,0 +1,50 @@
import logging
from .constants import (
DOWNLOAD_FILENAME_REGEX,
DOWNLOAD_URL_REGEX,
QUALITY_REGEX,
VIDEO_INFO_CLEAN_REGEX,
VIDEO_INFO_REGEX,
)
logger = logging.getLogger(__name__)
def extract_server_info(html_content: str, episode_title: str | None) -> dict | None:
"""
Extracts server information from the VixCloud/AnimeUnity embed page.
Handles extraction from both window.video object and download URL.
"""
video_info = VIDEO_INFO_REGEX.search(html_content)
download_url_match = DOWNLOAD_URL_REGEX.search(html_content)
if not (download_url_match and video_info):
return None
info_str = VIDEO_INFO_CLEAN_REGEX.sub(r'"\1"', video_info.group(1))
# Use eval context for JS constants
ctx = {"null": None, "true": True, "false": False}
try:
info = eval(info_str, ctx)
except Exception as e:
logger.error(f"Failed to parse JS object: {e}")
return None
download_url = download_url_match.group(1)
info["link"] = download_url
# Extract metadata from download URL if missing in window.video
if filename_match := DOWNLOAD_FILENAME_REGEX.search(download_url):
info["name"] = filename_match.group(1)
else:
info["name"] = f"{episode_title or 'Unknown'}"
if quality_match := QUALITY_REGEX.search(download_url):
# "720p" -> 720
info["quality"] = int(quality_match.group(1)[:-1])
else:
info["quality"] = 0 # Fallback
return info
@@ -99,7 +99,11 @@ def map_to_server(
translation_type=MediaTranslationType(translation_type),
mp4=True,
)
for quality in AVAILABLE_VIDEO_QUALITY
for quality in sorted(
list(set(AVAILABLE_VIDEO_QUALITY + [str(info["quality"])])),
key=lambda x: int(x),
reverse=True,
)
if int(quality) <= info["quality"]
],
episode_title=episode.title,
@@ -8,12 +8,11 @@ from ..types import Anime, AnimeEpisodeInfo, SearchResult, SearchResults
from ..utils.debug import debug_provider
from .constants import (
ANIMEUNITY_BASE,
DOWNLOAD_URL_REGEX,
MAX_TIMEOUT,
REPLACEMENT_WORDS,
TOKEN_REGEX,
VIDEO_INFO_REGEX,
)
from .extractor import extract_server_info
from .mappers import (
map_to_anime_result,
map_to_search_result,
@@ -158,14 +157,10 @@ class AnimeUnity(BaseAnimeProvider):
video_response = self.client.get(url=response.text.strip(), timeout=MAX_TIMEOUT)
video_response.raise_for_status()
video_info = VIDEO_INFO_REGEX.search(video_response.text)
download_url_match = DOWNLOAD_URL_REGEX.search(video_response.text)
if not (download_url_match and video_info):
if not (info := extract_server_info(video_response.text, episode.title)):
logger.error(f"Failed to extract video info for episode {episode.id}")
return None
info = eval(video_info.group(1).replace("null", "None"))
info["link"] = download_url_match.group(1)
yield map_to_server(episode, info, params.translation_type)