Skip to content

Instantly share code, notes, and snippets.

@salmiyounes
Last active November 25, 2024 15:29
A Python script that allows you to search for anime, select episodes, and stream them using the MPV player.
#!/usr/bin/env python3
from typing import Dict, Any, List
from anipy_api.provider.base import ProviderStream
from anipy_api.provider import list_providers, get_provider, LanguageTypeEnum
from anipy_api.anime import Anime
from pyfzf.pyfzf import FzfPrompt
from colorama import Fore, Style
from shutil import which
import os, subprocess
class Player:
@staticmethod
def mpv_check() -> bool:
return which('mpv') is not None
@staticmethod
def mpv_play_url(url: str, title: str, fullscreen: bool = True) -> None:
if Player.mpv_check():
mpv_command: List[str] = ['mpv']
if fullscreen:
mpv_command.append('--fs')
mpv_command.extend([f'--force-media-title={title}', url])
stdout = open(os.devnull, 'w')
subprocess.Popen(mpv_command, stdout=stdout, stderr=subprocess.STDOUT)
else:
raise NoSuchPlayerFound('MPV player not found on your system.')
class AnimeExtractor:
def __init__(self, search_result: Dict[str, Any], provider) -> None:
self.provider = provider()
self.search_results: List[Any] = [r for r in search_result.values()]
self.anime_list: List[Anime] = []
def get_anime_infos(self) -> List[Anime]:
for result in self.search_results:
self.anime_list.append(Anime.from_search_result(self.provider, result))
return self.anime_list
def list_episodes(self, anime: Anime, lang=LanguageTypeEnum.SUB) -> List[int]:
episodes: List[int] = anime.get_episodes(lang)
if episodes:
return episodes
raise EpisodesListError('No episodes have been found.\n')
def get_stream_url(self, anime: Anime, episode=1, lang=LanguageTypeEnum.SUB, quality=720) -> ProviderStream:
return anime.get_video(episode=episode, lang=lang, preferred_quality=quality)
def get_stream_urls(self, anime: Anime, lang=LanguageTypeEnum.SUB, episode=1) -> List[ProviderStream]:
streams: List[ProviderStream] = sorted(
anime.get_videos(episode=episode, lang=lang),
key=lambda stream: stream.resolution,
reverse=True,
)
if streams:
return streams
raise NoStreamUrl('No stream URL has been found.')
class Search:
def __init__(self, provider) -> None:
self.provider = provider
self.result = dict()
def list_search_result(self, search_query: str) -> Dict[str, Any]:
assert search_query is not None
self.result = {
search_result.name: search_result
for search_result in self.provider().get_search(search_query)
}
return self.result
def __getitem__(self, key: str) -> Any:
return self.result[key]
class ListProvider:
def __init__(self) -> None:
self.providers = {provider.NAME: provider for provider in list_providers()}
def __len__(self) -> int:
return len(self.providers)
def __getitem__(self, key: str) -> str:
return self.providers[key]
def __contains__(self, key: str) -> str:
return key in self.providers
class Main:
def __init__(self) -> None:
self.provider_list = ListProvider()
self.__call__()
def __call__(self) -> None:
self.main_loop()
return
def main_loop(self) -> None:
user_search: str = input(f'{Fore.BLUE}> Search for an anime: {Fore.GREEN}{Style.RESET_ALL}').strip()
assert user_search, "Please provide a valid query.\n"
if 'gogoanime' in self.provider_list:
provider = self.provider_list['gogoanime']
else:
provider = self.provider_list['yugenanime']
search_instance = Search(provider=provider)
search_results = search_instance.list_search_result(user_search)
if not search_results:
print(f'{Fore.RED}No results found. {Style.RESET_ALL}\n')
return
extractor = AnimeExtractor(provider=provider, search_result=search_results)
anime_list: Dict[str, Anime] = {anime.name: anime for anime in extractor.get_anime_infos()}
user_prompt: List[str] = FzfPrompt().prompt([name for name in anime_list.keys()], '--reverse')
assert user_prompt, "Please select an anime from the list."
anime = anime_list[user_prompt[0]]
assert anime, "Invalid anime selection."
episodes_list: List[int] = extractor.list_episodes(anime=anime)
episode: int = int(FzfPrompt().prompt(episodes_list, '--reverse')[0])
stream_urls: List[ProviderStream] = extractor.get_stream_urls(anime=anime, episode=episode)
player = Player()
player.mpv_play_url(stream_urls[0].url, title=f'{user_prompt[0]} - Episode: {episode}')
return
# Errors
class NoSuchPlayerFound(Exception):
pass
class NoStreamUrl(Exception):
pass
class EpisodesListError(Exception):
pass
if __name__ == '__main__':
Main()
anipy-api==3.2.3
beautifulsoup4==4.12.3
certifi==2024.8.30
charset-normalizer==3.4.0
colorama==0.4.6
dataclasses-json==0.6.7
idna==3.10
Levenshtein==0.25.1
m3u8==4.1.0
marshmallow==3.23.1
mypy-extensions==1.0.0
packaging==24.2
pycryptodomex==3.21.0
pyee==12.1.1
pyfzf==0.3.1
python-ffmpeg==2.0.12
python-mpv==1.0.7
RapidFuzz==3.10.1
requests==2.32.3
soupsieve==2.6
typing-inspect==0.9.0
typing_extensions==4.12.2
urllib3==2.2.3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment