diff --git a/viu_media/libs/provider/anime/animeunity/constants.py b/viu_media/libs/provider/anime/animeunity/constants.py index 8421a8d..7552685 100644 --- a/viu_media/libs/provider/anime/animeunity/constants.py +++ b/viu_media/libs/provider/anime/animeunity/constants.py @@ -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'(? 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 diff --git a/viu_media/libs/provider/anime/animeunity/mappers.py b/viu_media/libs/provider/anime/animeunity/mappers.py index e3b5a34..cd52b86 100644 --- a/viu_media/libs/provider/anime/animeunity/mappers.py +++ b/viu_media/libs/provider/anime/animeunity/mappers.py @@ -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, diff --git a/viu_media/libs/provider/anime/animeunity/provider.py b/viu_media/libs/provider/anime/animeunity/provider.py index 733b7dc..fed1b47 100644 --- a/viu_media/libs/provider/anime/animeunity/provider.py +++ b/viu_media/libs/provider/anime/animeunity/provider.py @@ -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)