Skip to content

Commit

Permalink
Merge pull request #195 from DragonOfShuu/master
Browse files Browse the repository at this point in the history
Addresses #193 & #196 (cli notifies when nothing to download/watch on seasonals) +more
  • Loading branch information
sdaqo authored Sep 13, 2024
2 parents e9a4b27 + f3cd8cd commit 0a21c0d
Show file tree
Hide file tree
Showing 12 changed files with 470 additions and 317 deletions.
37 changes: 37 additions & 0 deletions api/src/anipy_api/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,22 @@ def __init__(
self,
progress_callback: Optional[ProgressCallback] = None,
info_callback: Optional[InfoCallback] = None,
soft_error_callback: Optional[InfoCallback] = None,
):
"""__init__ of Downloader.
Args:
progress_callback: A callback with an percentage argument, that gets called on download progress.
info_callback: A callback with an message argument, that gets called on certain events.
soft_error_callback: A callback with a message argument, when certain events cause a non-fatal error (if none given, alternative fallback is info_callback).
"""
self._progress_callback: ProgressCallback = progress_callback or (
lambda percentage: None
)
self._info_callback: InfoCallback = info_callback or (lambda message: None)
self._soft_error_callback: InfoCallback = (
soft_error_callback or info_callback or (lambda message: None)
)

self._session = requests.Session()

Expand Down Expand Up @@ -250,6 +255,7 @@ def download(
download_path: Path,
container: Optional[str] = None,
ffmpeg: bool = False,
max_retry: int = 3,
) -> Path:
"""Generic download function that determines the best way to download a
specific stream and downloads it. The suffix should be omitted here,
Expand All @@ -267,10 +273,41 @@ def download(
Containers may include all containers supported by FFmpeg e.g. ".mp4", ".mkv" etc...
ffmpeg: Wheter to automatically default to
[ffmpeg_download][anipy_api.download.Downloader.ffmpeg_download] for m3u8/hls streams.
maxRetry: The amount of times the API can retry the download
Returns:
The path of the resulting file
"""
curr_exc: Exception | None = None
for i in range(max_retry):
try:
path = self._download_single_try(
stream, download_path, container, ffmpeg
)
return path
except DownloadError as e:
self._soft_error_callback(str(e))
curr_exc = e
except Exception as e:
self._soft_error_callback(f"An error occurred during download: {e}")
curr_exc = e
self._soft_error_callback(f"{max_retry-i-1} retries remain")

# Impossible, but to make the type
# checker happy
if curr_exc is None:
raise DownloadError("Unknown error occurred")
# If retrying doesn't work, double it and
# give it to the next exception handler
raise curr_exc

def _download_single_try(
self,
stream: "ProviderStream",
download_path: Path,
container: Optional[str] = None,
ffmpeg: bool = False,
) -> Path:
download_path.parent.mkdir(parents=True, exist_ok=True)

for p in download_path.parent.iterdir():
Expand Down
18 changes: 9 additions & 9 deletions api/src/anipy_api/provider/providers/gogo_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,27 +193,27 @@ def get_info(self, identifier: str) -> "ProviderInfoResult":
name = safe_attr(info_body.find("h1"), "text")
image = safe_attr(info_body.find("img"), "src")

alt_names = info_body.find("p", {"class": "other-name"}) # type: ignore
alt_names = info_body.find("p", {"class": "other-name"}) # type: ignore
if alt_names is not None:
alt_names = safe_attr(alt_names.find("a"), "text").split(",") # type: ignore
synopsis = safe_attr(info_body.find("div", {"class": "description"}), "text").replace("\n", "") # type: ignore

other_info = info_body.find_all("p", {"class": "type"}) # type: ignore
other_info = info_body.find_all("p", {"class": "type"}) # type: ignore
status, release_year, genres = None, None, []
for i in other_info:
cat_name = safe_attr(i.find("span"), "text")

if cat_name == "Genre:":
genres = [x["title"] for x in i.find_all("a")]
elif cat_name == "Status:":
status = safe_attr(i.find("a"), "text")
try:
status = Status[status.upper()] # type: ignore
except KeyError:
status = None
status = safe_attr(i.find("a"), "text")
try:
status = Status[status.upper()] # type: ignore
except KeyError:
status = None
elif cat_name == "Released:":
try:
release_year = int(safe_attr(i, "text").replace("Released: ", "")) # type: ignore
release_year = int(safe_attr(i, "text").replace("Released: ", "")) # type: ignore
except (ValueError, TypeError):
release_year = None

Expand Down
8 changes: 6 additions & 2 deletions api/src/anipy_api/provider/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from requests import Request, Session, Response
from bs4 import Tag, NavigableString


def request_page(session: "Session", req: "Request") -> "Response":
"""Prepare a request and send it.
Expand Down Expand Up @@ -40,11 +41,14 @@ def parsenum(n: str):
except ValueError:
return float(n)

def safe_attr(bs_obj: Optional[Union["Tag", "NavigableString", int]], attr: str) -> Optional[str]:

def safe_attr(
bs_obj: Optional[Union["Tag", "NavigableString", int]], attr: str
) -> Optional[str]:
if bs_obj is None or isinstance(bs_obj, int):
return None

if attr == "text":
return bs_obj.get_text()

return bs_obj.get(attr) # type: ignore
return bs_obj.get(attr) # type: ignore
Loading

0 comments on commit 0a21c0d

Please sign in to comment.