doc and style: formatted the whole codebase to pep8 plus added documentation where necessary

This commit is contained in:
Benedict Xavier Wanyonyi
2024-05-30 15:43:45 +03:00
parent e526e31083
commit b94987dfd7
54 changed files with 555 additions and 346 deletions

View File

@@ -1,12 +1,10 @@
import os
import time
import json
import re
import shutil
from subprocess import Popen, run, PIPE, CompletedProcess
from typing import Callable
from .extras import Logger
from .animdl_data_helper import (
filter_broken_streams,
@@ -23,7 +21,7 @@ from .animdl_exceptions import (
NoValidAnimeStreamsException,
Python310NotFoundException,
)
from .animdl_types import AnimdlAnimeUrlAndTitle, AnimdlData
from .animdl_types import AnimdlAnimeEpisode, AnimdlAnimeUrlAndTitle, AnimdlData
broken_link_pattern = r"https://tools.fast4speed.rsvp/\w*"
@@ -129,12 +127,25 @@ class AnimdlApi:
)
return most_likely_anime_url_and_title # ("title","anime url")
else:
raise AnimdlAnimeUrlNotFoundException
raise AnimdlAnimeUrlNotFoundException(
"The anime your searching for doesnt exist or animdl is broken or not in your system path"
)
@classmethod
def stream_anime_by_title_on_animdl(
cls, title, episodes_range=None, quality: str = "best"
cls, title: str, episodes_range: str | None = None, quality: str = "best"
) -> Popen:
"""Streams the anime title on animdl
Args:
title (str): the anime title you want to stream
episodes_range (str, optional): the episodes you want to stream; should be a valid animdl range. Defaults to None.
quality (str, optional): the quality of the stream. Defaults to "best".
Returns:
Popen: the stream child subprocess for mor control
"""
anime = cls.get_anime_url_by_title(title)
base_cmds = ["stream", anime[1], "-q", quality]
@@ -145,6 +156,17 @@ class AnimdlApi:
def stream_anime_with_mpv(
cls, title: str, episodes_range: str | None = None, quality: str = "best"
):
"""Stream an anime directly with mpv without having to interact with animdl cli
Args:
title (str): the anime title you want to stream
episodes_range (str | None, optional): a valid animdl episodes range you want ito watch. Defaults to None.
quality (str, optional): the quality of the stream. Defaults to "best".
Yields:
Popen: the child subprocess you currently are watching
"""
anime_data = cls.get_all_stream_urls_by_anime_title(title, episodes_range)
stream = []
for episode in anime_data.episodes:
@@ -186,8 +208,18 @@ class AnimdlApi:
@classmethod
def get_all_anime_stream_urls_by_anime_url(
cls, anime_url: str, episodes_range=None
):
cls, anime_url: str, episodes_range: str | None = None
) -> list[AnimdlAnimeEpisode]:
"""gets all the streams for the animdl url
Args:
anime_url (str): an animdl url used in scraping
episodes_range (str | None, optional): a valid animdl episodes range. Defaults to None.
Returns:
list[AnimdlAnimeEpisode]: A list of anime episodes gotten from animdl
"""
cmd = (
["grab", anime_url, "-r", episodes_range]
if episodes_range
@@ -207,8 +239,9 @@ class AnimdlApi:
episodes_range (str, optional): an animdl episodes range. Defaults to None.
Returns:
_type_: _description_
AnimdlData: The parsed data from animdl grab
"""
possible_anime = cls.get_anime_url_by_title(title)
return AnimdlData(
possible_anime.anime_title,
@@ -235,6 +268,23 @@ class AnimdlApi:
episodes_range: str | None = None,
quality: str = "best",
) -> tuple[list[int], list[int]]:
"""Downloads anime either adaptive, progressive, or .m3u streams and uses mpv to achieve this
Args:
_anime_title (str): the anime title you want to download
on_episode_download_progress (Callable): the callback when a chunk of an episode is downloaded
on_episode_download_complete (Callable): the callback when an episode has been successfully downloaded
on_complete (Callable): callback when the downloading process is complete
output_path (str): the directory | folder to download the anime
episodes_range (str | None, optional): a valid animdl episode range. Defaults to None.
quality (str, optional): the anime quality. Defaults to "best".
Raises:
NoValidAnimeStreamsException: raised when no valid streams were found for a particular episode
Returns:
tuple[list[int], list[int]]: a tuple containing successful, and failed downloads list
"""
anime_streams_data = cls.get_all_stream_urls_by_anime_title(
_anime_title, episodes_range
@@ -260,6 +310,11 @@ class AnimdlApi:
streams = filter_broken_streams(episode["streams"])
# raises an exception if no streams for current episodes
if not streams:
raise NoValidAnimeStreamsException(
f"No valid streams were found for episode {episode_number}"
)
episode_stream = filter_streams_by_quality(streams, quality)
# determine episode_title
@@ -340,6 +395,17 @@ class AnimdlApi:
@classmethod
def download_with_mpv(cls, url: str, output_path: str, on_progress: Callable):
"""The method used to download a remote resource with mpv
Args:
url (str): the url of the remote resource to download
output_path (str): the location to download the resource to
on_progress (Callable): the callback when a chunk of the resource is downloaded
Returns:
subprocess return code: the return code of the mpv subprocess
"""
mpv_child_process = run_mpv_command(url, f"--stream-dump={output_path}")
progress_regex = re.compile(r"\d+/\d+") # eg Dumping 2044776/125359745
@@ -361,6 +427,18 @@ class AnimdlApi:
episode_info: dict[str, str],
on_progress: Callable,
):
"""the progressive downloader of mpv
Args:
video_url (str): a video url
output_path (str): download location
episode_info (dict[str, str]): the details of the episode we downloading
on_progress (Callable): the callback when a chunk is downloaded
Raises:
Exception: exception raised when anything goes wrong
"""
episode = (
path_parser(episode_info["anime_title"])
+ " - "
@@ -385,6 +463,20 @@ class AnimdlApi:
on_progress: Callable,
episode_info: dict[str, str],
):
"""the adaptive downloader
Args:
video_url (str): url of video you want ot download
audio_url (str): url of audio file you want ot download
sub_url (str): url of sub file you want ot download
output_path (str): download location
on_progress (Callable): the callback when a chunk is downloaded
episode_info (dict[str, str]): episode details
Raises:
Exception: incase anything goes wrong
"""
on_progress_ = lambda current_bytes, total_bytes: on_progress(
current_bytes, total_bytes, episode_info
)
@@ -421,6 +513,19 @@ class AnimdlApi:
on_progress: Callable,
episode_info: dict[str, str],
):
"""only downloads video and subs
Args:
video_url (str): url of video you want ot download
sub_url (str): url of sub you want ot download
output_path (str): the download location
on_progress (Callable): the callback for when a chunk is downloaded
episode_info (dict[str, str]): episode details
Raises:
Exception: when anything goes wrong
"""
on_progress_ = lambda current_bytes, total_bytes: on_progress(
current_bytes, total_bytes, episode_info
)
@@ -441,21 +546,3 @@ class AnimdlApi:
if is_video_failure:
raise Exception
# TODO: ADD RUN_MPV_COMMAND = RAISES MPV NOT FOR ND EXCEPTION
# TODO: ADD STREAM WITH MPV
if __name__ == "__main__":
title = input("enter title: ")
e_range = input("enter range: ")
start = time.time()
# t = AnimdlApi.download_anime_by_title(
# title, lambda *u: print(u), lambda *u: print(u)
# ,lambda *u:print(u),".",episodes_range=e_range)
streamer = AnimdlApi.stream_anime_with_mpv(title, e_range, quality="720")
# with open("test.json","w") as file:
# print(json.dump(t,file))
for stream in streamer:
print(stream.communicate())
delta = time.time() - start
print(f"Took: {delta} secs")

View File

@@ -4,7 +4,11 @@ import json
from fuzzywuzzy import fuzz
from .extras import Logger
from .animdl_types import AnimdlAnimeUrlAndTitle,AnimdlData,AnimdlAnimeEpisode,AnimdlEpisodeStream
from .animdl_types import (
AnimdlAnimeUrlAndTitle,
AnimdlAnimeEpisode,
AnimdlEpisodeStream,
)
# Currently this links don't work so we filter it out
@@ -70,6 +74,15 @@ def anime_title_percentage_match(
def filter_broken_streams(
streams: list[AnimdlEpisodeStream],
) -> list[AnimdlEpisodeStream]:
"""filters the streams that the project has evaluated doesnt work
Args:
streams (list[AnimdlEpisodeStream]): the streams to filter
Returns:
list[AnimdlEpisodeStream]: the valid streams
"""
stream_filter = lambda stream: (
True if not re.match(broken_link_pattern, stream["stream_url"]) else False
)
@@ -77,9 +90,19 @@ def filter_broken_streams(
def filter_streams_by_quality(
anime_episode_streams: list[AnimdlEpisodeStream], quality: str|int, strict=False
anime_episode_streams: list[AnimdlEpisodeStream], quality: str | int, strict=False
) -> AnimdlEpisodeStream:
# filtered_streams = []
"""filters streams by quality
Args:
anime_episode_streams (list[AnimdlEpisodeStream]): the streams to filter
quality (str | int): the quality you want to get
strict (bool, optional): whether to always return an episode if quality isn,t found. Defaults to False.
Returns:
AnimdlEpisodeStream: the stream of specified quality
"""
# get the appropriate stream or default to best
get_quality_func = lambda stream_: (
stream_.get("quality") if stream_.get("quality") else 0
@@ -104,8 +127,16 @@ def filter_streams_by_quality(
# return AnimdlEpisodeStream({})
# TODO: add typing to return dict
def parse_stream_urls_data(raw_stream_urls_data: str) -> list[AnimdlAnimeEpisode]:
"""parses the streams data gotten from animdl grab
Args:
raw_stream_urls_data (str): the animdl grab data to parse
Returns:
list[AnimdlAnimeEpisode]: the parsed streams for all episode
"""
try:
return [
AnimdlAnimeEpisode(json.loads(episode.strip()))
@@ -123,8 +154,9 @@ def search_output_parser(raw_data: str) -> list[AnimdlAnimeUrlAndTitle]:
raw_data (str): valid animdl data
Returns:
dict: parsed animdl data containing an anime title
AnimdlAnimeUrlAndTitle: parsed animdl data containing an animdl anime url and anime title
"""
# get each line of dat and ignore those that contain unwanted data
data = raw_data.split("\n")[3:]

View File

@@ -1 +0,0 @@
["jujutsu kaisen", [{"episode": 1, "streams": [{"stream_url": "https://tools.fast4speed.rsvp//media6/videos/CRZx43dgcfpWecx7W/sub/1"}]}, {"episode": 2, "streams": [{"stream_url": "https://tools.fast4speed.rsvp//media6/videos/CRZx43dgcfpWecx7W/sub/2"}]}, {"episode": 3, "streams": [{"stream_url": "https://tools.fast4speed.rsvp//media6/videos/CRZx43dgcfpWecx7W/sub/3"}]}]]