mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-07 21:30:38 -08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bc0e52179 | ||
|
|
40f1c4fba5 | ||
|
|
454341eaf5 | ||
|
|
abab2540a3 | ||
|
|
b2bc8cbace | ||
|
|
90bbf3c033 | ||
|
|
ac91b1770a | ||
|
|
19d42b7924 | ||
|
|
9ec3136734 | ||
|
|
943fca43cf | ||
|
|
b2e00feb94 | ||
|
|
f726c8d55c |
@@ -230,12 +230,12 @@ Example usage of the above options
|
||||
|
||||
```bash
|
||||
# example of syncplay intergration
|
||||
fastanime --sync-play search -t <anime-title>
|
||||
fastanime --sync-play --server sharepoint search -t <anime-title>
|
||||
|
||||
# --- or ---
|
||||
|
||||
# to watch with anilist intergration
|
||||
fastanime --sync-play anilist
|
||||
fastanime --sync-play --server sharepoint anilist
|
||||
|
||||
# downloading dubbed anime
|
||||
fastanime --dub download <anime>
|
||||
@@ -521,6 +521,10 @@ continue_from_history = True # Auto continue from watch history
|
||||
# which history to use [local/remote]
|
||||
preferred_history = local
|
||||
|
||||
# force mpv window
|
||||
# passed directly to mpv so values are same
|
||||
force_window = immediate
|
||||
|
||||
translation_type = sub # Preferred language for anime (options: dub, sub)
|
||||
|
||||
server = top # Default server (options: dropbox, sharepoint, wetransfer.gogoanime, top, wixmp)
|
||||
|
||||
@@ -35,6 +35,9 @@ class YtDLPDownloader:
|
||||
download_dir: str,
|
||||
silent: bool,
|
||||
vid_format: str = "best",
|
||||
force_unknown_ext=False,
|
||||
verbose=False,
|
||||
headers={},
|
||||
):
|
||||
"""Helper function that downloads anime given url and path details
|
||||
|
||||
@@ -50,10 +53,12 @@ class YtDLPDownloader:
|
||||
episode_title = sanitize_filename(episode_title)
|
||||
ydl_opts = {
|
||||
# Specify the output path and template
|
||||
"http_headers": headers,
|
||||
"outtmpl": f"{download_dir}/{anime_title}/{episode_title}.%(ext)s",
|
||||
"silent": silent,
|
||||
"verbose": False,
|
||||
"verbose": verbose,
|
||||
"format": vid_format,
|
||||
"compat_opts": ("allow-unsafe-ext",) if force_unknown_ext else tuple(),
|
||||
}
|
||||
|
||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||
|
||||
@@ -11,6 +11,13 @@ if TYPE_CHECKING:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def sort_by_episode_number(filename: str):
|
||||
import re
|
||||
|
||||
match = re.search(r"\d+", filename)
|
||||
return int(match.group()) if match else 0
|
||||
|
||||
|
||||
def anime_title_percentage_match(
|
||||
possible_user_requested_anime_title: str, anime: "AnilistBaseMediaDataSchema"
|
||||
) -> float:
|
||||
|
||||
@@ -6,7 +6,7 @@ if sys.version_info < (3, 10):
|
||||
) # noqa: F541
|
||||
|
||||
|
||||
__version__ = "v2.0.0"
|
||||
__version__ = "v2.1.0"
|
||||
|
||||
APP_NAME = "FastAnime"
|
||||
AUTHOR = "Benex254"
|
||||
|
||||
@@ -26,11 +26,28 @@ if TYPE_CHECKING:
|
||||
"-r",
|
||||
help="A range of episodes to download (start-end)",
|
||||
)
|
||||
@click.option(
|
||||
"--force-unknown-ext",
|
||||
"-f",
|
||||
help="This option forces yt-dlp to download extensions its not aware of",
|
||||
is_flag=True,
|
||||
)
|
||||
@click.option(
|
||||
"--silent/--no-silent",
|
||||
"-q/-V",
|
||||
type=bool,
|
||||
help="Download silently (during download)",
|
||||
default=True,
|
||||
)
|
||||
@click.option("--verbose", "-v", is_flag=True, help="Download verbosely (everywhere)")
|
||||
@click.pass_obj
|
||||
def download(
|
||||
config: "Config",
|
||||
anime_titles: list,
|
||||
episode_range,
|
||||
force_unknown_ext,
|
||||
silent,
|
||||
verbose,
|
||||
):
|
||||
from rich import print
|
||||
from rich.progress import Progress
|
||||
@@ -61,9 +78,7 @@ def download(
|
||||
print("Search results failed")
|
||||
input("Enter to retry")
|
||||
download(
|
||||
config,
|
||||
anime_title,
|
||||
episode_range,
|
||||
config, anime_title, episode_range, force_unknown_ext, silent, verbose
|
||||
)
|
||||
return
|
||||
search_results = search_results["results"]
|
||||
@@ -96,15 +111,14 @@ def download(
|
||||
print("Sth went wring anime no found")
|
||||
input("Enter to continue...")
|
||||
download(
|
||||
config,
|
||||
anime_title,
|
||||
episode_range,
|
||||
config, anime_title, episode_range, force_unknown_ext, silent, verbose
|
||||
)
|
||||
return
|
||||
|
||||
episodes = sorted(
|
||||
anime["availableEpisodesDetail"][config.translation_type], key=float
|
||||
)
|
||||
# where the magic happens
|
||||
if episode_range:
|
||||
if ":" in episode_range:
|
||||
ep_range_tuple = episode_range.split(":")
|
||||
@@ -131,6 +145,7 @@ def download(
|
||||
else:
|
||||
episodes_range = sorted(episodes, key=float)
|
||||
|
||||
# lets download em
|
||||
for episode in episodes_range:
|
||||
try:
|
||||
episode = str(episode)
|
||||
@@ -159,6 +174,7 @@ def download(
|
||||
input("Enter to continue")
|
||||
continue
|
||||
link = stream_link["link"]
|
||||
provider_headers = server["headers"]
|
||||
episode_title = server["episode_title"]
|
||||
else:
|
||||
with Progress() as progress:
|
||||
@@ -183,6 +199,7 @@ def download(
|
||||
print("Quality not found")
|
||||
continue
|
||||
link = stream_link["link"]
|
||||
provider_headers = servers[server]["headers"]
|
||||
|
||||
episode_title = servers[server]["episode_title"]
|
||||
print(f"[purple]Now Downloading:[/] {search_result} Episode {episode}")
|
||||
@@ -192,8 +209,11 @@ def download(
|
||||
anime["title"],
|
||||
episode_title,
|
||||
download_dir,
|
||||
True,
|
||||
silent,
|
||||
config.format,
|
||||
force_unknown_ext,
|
||||
verbose,
|
||||
headers=provider_headers,
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
@@ -27,6 +27,7 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
from ...cli.utils.mpv import run_mpv
|
||||
from ...libs.fzf import fzf
|
||||
from ...libs.rofi import Rofi
|
||||
from ...Utility.utils import sort_by_episode_number
|
||||
from ..utils.tools import exit_app
|
||||
from ..utils.utils import fuzzy_inquirer
|
||||
|
||||
@@ -39,7 +40,9 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
if not os.path.exists(USER_VIDEOS_DIR):
|
||||
print("Downloads directory specified does not exist")
|
||||
return
|
||||
anime_downloads = sorted(os.listdir(USER_VIDEOS_DIR))
|
||||
anime_downloads = sorted(
|
||||
os.listdir(USER_VIDEOS_DIR),
|
||||
)
|
||||
anime_downloads.append("Exit")
|
||||
|
||||
def create_thumbnails(video_path, anime_title, downloads_thumbnail_cache_dir):
|
||||
@@ -76,6 +79,7 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
|
||||
def get_previews_anime(workers=None, bg=True):
|
||||
import concurrent.futures
|
||||
import random
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
@@ -99,10 +103,16 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
anime_path = os.path.join(USER_VIDEOS_DIR, anime_title)
|
||||
if not os.path.isdir(anime_path):
|
||||
continue
|
||||
playlist = sorted(os.listdir(anime_path))
|
||||
playlist = [
|
||||
anime
|
||||
for anime in sorted(
|
||||
os.listdir(anime_path),
|
||||
)
|
||||
if "mp4" in anime
|
||||
]
|
||||
if playlist:
|
||||
# actual link to download image from
|
||||
video_path = os.path.join(anime_path, playlist[0])
|
||||
video_path = os.path.join(anime_path, random.choice(playlist))
|
||||
future_to_url[
|
||||
executor.submit(
|
||||
create_thumbnails,
|
||||
@@ -166,7 +176,9 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
# anime_playlist_path = os.path.join(USER_VIDEOS_DIR, anime_playlist_path)
|
||||
if not os.path.isdir(anime_playlist_path):
|
||||
return
|
||||
anime_episodes = sorted(os.listdir(anime_playlist_path))
|
||||
anime_episodes = sorted(
|
||||
os.listdir(anime_playlist_path), key=sort_by_episode_number
|
||||
)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
|
||||
# load the jobs
|
||||
future_to_url = {}
|
||||
@@ -223,7 +235,9 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
print(anime_playlist_path, "is not dir")
|
||||
exit_app(1)
|
||||
return
|
||||
episodes = sorted(os.listdir(anime_playlist_path))
|
||||
episodes = sorted(
|
||||
os.listdir(anime_playlist_path), key=sort_by_episode_number
|
||||
)
|
||||
downloaded_episodes = [*episodes, "Back"]
|
||||
if config.use_fzf:
|
||||
if not config.preview:
|
||||
@@ -249,7 +263,12 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
stream_anime()
|
||||
return
|
||||
episode_path = os.path.join(anime_playlist_path, episode_title)
|
||||
run_mpv(episode_path)
|
||||
if config.sync_play:
|
||||
from ..utils.syncplay import SyncPlayer
|
||||
|
||||
SyncPlayer(episode_path)
|
||||
else:
|
||||
run_mpv(episode_path)
|
||||
stream_episode(anime_playlist_path)
|
||||
|
||||
def stream_anime():
|
||||
@@ -282,7 +301,12 @@ def downloads(config: "Config", path: bool, view_episodes, ffmpegthumbnailer_see
|
||||
playlist,
|
||||
)
|
||||
else:
|
||||
run_mpv(playlist)
|
||||
if config.sync_play:
|
||||
from ..utils.syncplay import SyncPlayer
|
||||
|
||||
SyncPlayer(playlist)
|
||||
else:
|
||||
run_mpv(playlist)
|
||||
stream_anime()
|
||||
|
||||
stream_anime()
|
||||
|
||||
@@ -176,6 +176,7 @@ def search(config: Config, anime_titles: str, episode_range: str):
|
||||
stream_anime()
|
||||
return
|
||||
link = stream_link["link"]
|
||||
stream_headers = server["headers"]
|
||||
episode_title = server["episode_title"]
|
||||
else:
|
||||
with Progress() as progress:
|
||||
@@ -204,16 +205,17 @@ def search(config: Config, anime_titles: str, episode_range: str):
|
||||
stream_anime()
|
||||
return
|
||||
link = stream_link["link"]
|
||||
stream_headers = servers[server]["headers"]
|
||||
episode_title = servers[server]["episode_title"]
|
||||
print(f"[purple]Now Playing:[/] {search_result} Episode {episode}")
|
||||
|
||||
if config.sync_play:
|
||||
from ..utils.syncplay import SyncPlayer
|
||||
|
||||
SyncPlayer(link, episode_title)
|
||||
SyncPlayer(link, episode_title, headers=stream_headers)
|
||||
else:
|
||||
run_mpv(link, episode_title)
|
||||
except Exception as e:
|
||||
run_mpv(link, episode_title, headers=stream_headers)
|
||||
except IndexError as e:
|
||||
print(e)
|
||||
input("Enter to continue")
|
||||
stream_anime()
|
||||
|
||||
@@ -296,6 +296,10 @@ error = {self.error}
|
||||
# adding more options to it
|
||||
use_mpv_mod = {self.use_mpv_mod}
|
||||
|
||||
# force mpv window
|
||||
# passed directly to mpv so values are same
|
||||
force_window = immediate
|
||||
|
||||
# the format of downloaded anime and trailer
|
||||
# based on yt-dlp format and passed directly to it
|
||||
# learn more by looking it up on their site
|
||||
|
||||
@@ -117,7 +117,9 @@ def media_player_controls(
|
||||
from ..utils.syncplay import SyncPlayer
|
||||
|
||||
stop_time, total_time = SyncPlayer(
|
||||
current_episode_stream_link, selected_server["episode_title"]
|
||||
current_episode_stream_link,
|
||||
selected_server["episode_title"],
|
||||
headers=selected_server["headers"],
|
||||
)
|
||||
elif config.use_mpv_mod:
|
||||
from ..utils.player import player
|
||||
@@ -128,6 +130,7 @@ def media_player_controls(
|
||||
fastanime_runtime_state,
|
||||
config,
|
||||
selected_server["episode_title"],
|
||||
headers=selected_server["headers"],
|
||||
)
|
||||
|
||||
# TODO: implement custom aniskip
|
||||
@@ -148,6 +151,7 @@ def media_player_controls(
|
||||
selected_server["episode_title"],
|
||||
start_time=start_time,
|
||||
custom_args=custom_args,
|
||||
headers=selected_server["headers"],
|
||||
)
|
||||
|
||||
# either update the watch history to the next episode or current depending on progress
|
||||
@@ -509,7 +513,9 @@ def provider_anime_episode_servers_menu(
|
||||
from ..utils.syncplay import SyncPlayer
|
||||
|
||||
stop_time, total_time = SyncPlayer(
|
||||
current_stream_link, selected_server["episode_title"]
|
||||
current_stream_link,
|
||||
selected_server["episode_title"],
|
||||
headers=selected_server["headers"],
|
||||
)
|
||||
elif config.use_mpv_mod:
|
||||
from ..utils.player import player
|
||||
@@ -520,6 +526,7 @@ def provider_anime_episode_servers_menu(
|
||||
fastanime_runtime_state,
|
||||
config,
|
||||
selected_server["episode_title"],
|
||||
headers=selected_server["headers"],
|
||||
)
|
||||
|
||||
# TODO: implement custom aniskip intergration
|
||||
@@ -543,6 +550,7 @@ def provider_anime_episode_servers_menu(
|
||||
selected_server["episode_title"],
|
||||
start_time=start_time,
|
||||
custom_args=custom_args,
|
||||
headers=selected_server["headers"],
|
||||
)
|
||||
print("Finished at: ", stop_time)
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from fastanime.constants import S_PLATFORM
|
||||
|
||||
|
||||
def stream_video(MPV, url, mpv_args, custom_args):
|
||||
process = subprocess.Popen(
|
||||
@@ -52,6 +54,7 @@ def run_mpv(
|
||||
start_time: str = "0",
|
||||
ytdl_format="",
|
||||
custom_args=[],
|
||||
headers={},
|
||||
):
|
||||
# Determine if mpv is available
|
||||
MPV = shutil.which("mpv")
|
||||
@@ -61,7 +64,7 @@ def run_mpv(
|
||||
# Regex to check if the link is a YouTube URL
|
||||
youtube_regex = r"(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/.+"
|
||||
|
||||
if not MPV:
|
||||
if not MPV and not S_PLATFORM == "win32":
|
||||
# Determine if the link is a YouTube URL
|
||||
if re.match(youtube_regex, link):
|
||||
# Android specific commands to launch mpv with a YouTube URL
|
||||
@@ -100,6 +103,11 @@ def run_mpv(
|
||||
else:
|
||||
# General mpv command with custom arguments
|
||||
mpv_args = []
|
||||
if headers:
|
||||
mpv_headers = "--http-header-fields="
|
||||
for header_name, header_value in headers.items():
|
||||
mpv_headers += f"{header_name}:{header_value},"
|
||||
mpv_args.append(mpv_headers)
|
||||
if start_time != "0":
|
||||
mpv_args.append(f"--start={start_time}")
|
||||
if title:
|
||||
|
||||
@@ -151,6 +151,7 @@ class MpvPlayer(object):
|
||||
fastanime_runtime_state,
|
||||
config: "Config",
|
||||
title,
|
||||
headers={},
|
||||
):
|
||||
self.anime_provider = anime_provider
|
||||
self.fastanime_runtime_state = fastanime_runtime_state
|
||||
@@ -174,6 +175,11 @@ class MpvPlayer(object):
|
||||
# mpv_player.cache = "yes"
|
||||
# mpv_player.cache_pause = "no"
|
||||
mpv_player.title = title
|
||||
mpv_headers = ""
|
||||
if headers:
|
||||
for header_name, header_value in headers.items():
|
||||
mpv_headers += f"{header_name}:{header_value},"
|
||||
mpv_player.http_header_fields = mpv_headers
|
||||
|
||||
mpv_player.play(stream_link)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import subprocess
|
||||
from .tools import exit_app
|
||||
|
||||
|
||||
def SyncPlayer(url: str, anime_title, *args):
|
||||
def SyncPlayer(url: str, anime_title=None, headers={}, *args):
|
||||
# TODO: handle m3u8 multi quality streams
|
||||
#
|
||||
# check for SyncPlay
|
||||
@@ -14,8 +14,29 @@ def SyncPlayer(url: str, anime_title, *args):
|
||||
exit_app(1)
|
||||
return "0", "0"
|
||||
# start SyncPlayer
|
||||
subprocess.run(
|
||||
[SYNCPLAY_EXECUTABLE, url, "--", f"--force-media-title={anime_title}"]
|
||||
)
|
||||
mpv_args = []
|
||||
if headers:
|
||||
mpv_headers = "--http-header-fields="
|
||||
for header_name, header_value in headers.items():
|
||||
mpv_headers += f"{header_name}:{header_value},"
|
||||
mpv_args.append(mpv_headers)
|
||||
if not anime_title:
|
||||
subprocess.run(
|
||||
[
|
||||
SYNCPLAY_EXECUTABLE,
|
||||
url,
|
||||
]
|
||||
)
|
||||
else:
|
||||
subprocess.run(
|
||||
[
|
||||
SYNCPLAY_EXECUTABLE,
|
||||
url,
|
||||
"--",
|
||||
f"--force-media-title={anime_title}",
|
||||
*mpv_args,
|
||||
]
|
||||
)
|
||||
|
||||
# for compatability
|
||||
return "0", "0"
|
||||
|
||||
@@ -9,4 +9,5 @@ SERVERS_AVAILABLE = [
|
||||
"weTransfer",
|
||||
"wixmp",
|
||||
"kwik",
|
||||
"Yt",
|
||||
]
|
||||
|
||||
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING
|
||||
from requests.exceptions import Timeout
|
||||
|
||||
from ...anime_provider.base_provider import AnimeProvider
|
||||
from ..utils import decode_hex_string, give_random_quality
|
||||
from ..utils import give_random_quality, one_digit_symmetric_xor
|
||||
from .constants import (
|
||||
ALLANIME_API_ENDPOINT,
|
||||
ALLANIME_BASE,
|
||||
@@ -205,23 +205,45 @@ class AllAnimeAPI(AnimeProvider):
|
||||
# filter the working streams no need to get all since the others are mostly hsl
|
||||
# TODO: should i just get all the servers and handle the hsl??
|
||||
if embed.get("sourceName", "") not in (
|
||||
"Sak",
|
||||
"Kir",
|
||||
"S-mp4",
|
||||
"Luf-mp4",
|
||||
"Default",
|
||||
# priorities based on death note
|
||||
"Sak", # 7
|
||||
"S-mp4", # 7.9
|
||||
"Luf-mp4", # 7.7
|
||||
"Default", # 8.5
|
||||
"Yt-mp4", # 7.9
|
||||
"Kir", # NA
|
||||
# "Vid-mp4" # 4
|
||||
# "Ok", # 3.5
|
||||
# "Ss-Hls", # 5.5
|
||||
# "Mp4", # 4
|
||||
):
|
||||
continue
|
||||
url = embed.get("sourceUrl")
|
||||
|
||||
#
|
||||
if not url:
|
||||
continue
|
||||
if url.startswith("--"):
|
||||
url = url[2:]
|
||||
url = one_digit_symmetric_xor(56, url)
|
||||
|
||||
if "tools.fast4speed.rsvp" in url:
|
||||
yield {
|
||||
"server": "Yt",
|
||||
"episode_title": f'{anime["title"]}; Episode {episode_number}',
|
||||
"headers": {"Referer": f"https://{ALLANIME_BASE}/"},
|
||||
"links": [
|
||||
{
|
||||
"link": url,
|
||||
"quality": "1080",
|
||||
}
|
||||
],
|
||||
} # pyright:ignore
|
||||
continue
|
||||
|
||||
# get the stream url for an episode of the defined source names
|
||||
parsed_url = decode_hex_string(url)
|
||||
embed_url = f"https://{ALLANIME_BASE}{parsed_url.replace('clock', 'clock.json')}"
|
||||
embed_url = (
|
||||
f"https://{ALLANIME_BASE}{url.replace('clock', 'clock.json')}"
|
||||
)
|
||||
resp = self.session.get(
|
||||
embed_url,
|
||||
headers={
|
||||
@@ -230,12 +252,14 @@ class AllAnimeAPI(AnimeProvider):
|
||||
},
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
if resp.status_code == 200:
|
||||
match embed["sourceName"]:
|
||||
case "Luf-mp4":
|
||||
logger.debug("allanime:Found streams from gogoanime")
|
||||
yield {
|
||||
"server": "gogoanime",
|
||||
"headers": {},
|
||||
"episode_title": (
|
||||
allanime_episode["notes"] or f'{anime["title"]}'
|
||||
)
|
||||
@@ -246,6 +270,7 @@ class AllAnimeAPI(AnimeProvider):
|
||||
logger.debug("allanime:Found streams from wetransfer")
|
||||
yield {
|
||||
"server": "wetransfer",
|
||||
"headers": {},
|
||||
"episode_title": (
|
||||
allanime_episode["notes"] or f'{anime["title"]}'
|
||||
)
|
||||
@@ -256,6 +281,7 @@ class AllAnimeAPI(AnimeProvider):
|
||||
logger.debug("allanime:Found streams from sharepoint")
|
||||
yield {
|
||||
"server": "sharepoint",
|
||||
"headers": {},
|
||||
"episode_title": (
|
||||
allanime_episode["notes"] or f'{anime["title"]}'
|
||||
)
|
||||
@@ -266,6 +292,7 @@ class AllAnimeAPI(AnimeProvider):
|
||||
logger.debug("allanime:Found streams from dropbox")
|
||||
yield {
|
||||
"server": "dropbox",
|
||||
"headers": {},
|
||||
"episode_title": (
|
||||
allanime_episode["notes"] or f'{anime["title"]}'
|
||||
)
|
||||
@@ -276,20 +303,22 @@ class AllAnimeAPI(AnimeProvider):
|
||||
logger.debug("allanime:Found streams from wixmp")
|
||||
yield {
|
||||
"server": "wixmp",
|
||||
"headers": {},
|
||||
"episode_title": (
|
||||
allanime_episode["notes"] or f'{anime["title"]}'
|
||||
)
|
||||
+ f"; Episode {episode_number}",
|
||||
"links": give_random_quality(resp.json()["links"]),
|
||||
} # pyright:ignore
|
||||
|
||||
except Timeout:
|
||||
logger.error(
|
||||
"Timeout has been exceeded this could mean allanime is down or you have lost internet connection"
|
||||
)
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"FA(Allanime): {e}")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"FA(Allanime): {e}")
|
||||
return []
|
||||
@@ -301,7 +330,7 @@ if __name__ == "__main__":
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from InquirerPy import inquirer, validator
|
||||
from InquirerPy import inquirer, validator # pyright:ignore
|
||||
|
||||
anime = input("Enter the anime name: ")
|
||||
translation = input("Enter the translation type: ")
|
||||
|
||||
@@ -183,7 +183,12 @@ class AnimePaheApi(AnimeProvider):
|
||||
episode["title"] or f"{anime['title']}; Episode {episode['episode']}"
|
||||
)
|
||||
# get all links
|
||||
streams = {"server": "kwik", "links": [], "episode_title": episode_title}
|
||||
streams = {
|
||||
"server": "kwik",
|
||||
"links": [],
|
||||
"episode_title": episode_title,
|
||||
"headers": {},
|
||||
}
|
||||
for res_dict in res_dicts:
|
||||
# get embed url
|
||||
embed_url = res_dict["data-src"]
|
||||
|
||||
@@ -60,12 +60,12 @@ class EpisodeStream(TypedDict):
|
||||
hls: bool | None
|
||||
mp4: bool | None
|
||||
priority: int | None
|
||||
headers: dict | None
|
||||
quality: Literal["360", "720", "1080", "unknown"]
|
||||
translation_type: Literal["dub", "sub"]
|
||||
|
||||
|
||||
class Server(TypedDict):
|
||||
headers: dict
|
||||
server: str
|
||||
episode_title: str
|
||||
links: list[EpisodeStream]
|
||||
|
||||
@@ -44,6 +44,14 @@ def give_random_quality(links: list[dict]):
|
||||
]
|
||||
|
||||
|
||||
def one_digit_symmetric_xor(password: int, target: str):
|
||||
def genexp():
|
||||
for segment in bytearray.fromhex(target):
|
||||
yield segment ^ password
|
||||
|
||||
return bytes(genexp()).decode("utf-8")
|
||||
|
||||
|
||||
def decode_hex_string(hex_string):
|
||||
"""some of the sources encrypt the urls into hex codes this function decrypts the urls
|
||||
|
||||
|
||||
12
poetry.lock
generated
12
poetry.lock
generated
@@ -845,13 +845,13 @@ testing = ["covdefaults (>=2.3)", "pytest (>=8.2.2)", "pytest-cov (>=5)", "pytes
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.375"
|
||||
version = "1.1.376"
|
||||
description = "Command line wrapper for pyright"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pyright-1.1.375-py3-none-any.whl", hash = "sha256:4c5e27eddeaee8b41cc3120736a1dda6ae120edf8523bb2446b6073a52f286e3"},
|
||||
{file = "pyright-1.1.375.tar.gz", hash = "sha256:7765557b0d6782b2fadabff455da2014476404c9e9214f49977a4e49dec19a0f"},
|
||||
{file = "pyright-1.1.376-py3-none-any.whl", hash = "sha256:0f2473b12c15c46b3207f0eec224c3cea2bdc07cd45dd4a037687cbbca0fbeff"},
|
||||
{file = "pyright-1.1.376.tar.gz", hash = "sha256:bffd63b197cd0810395bb3245c06b01f95a85ddf6bfa0e5644ed69c841e954dd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1157,13 +1157,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "tox"
|
||||
version = "4.17.1"
|
||||
version = "4.18.0"
|
||||
description = "tox is a generic virtualenv management and test command line tool"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tox-4.17.1-py3-none-any.whl", hash = "sha256:2974597c0353577126ab014f52d1a399fb761049e165ff34427f84e8cfe6c990"},
|
||||
{file = "tox-4.17.1.tar.gz", hash = "sha256:2c41565a571e34480bd401d668a4899806169a4633e972ac296c54406d2ded8a"},
|
||||
{file = "tox-4.18.0-py3-none-any.whl", hash = "sha256:0a457400cf70615dc0627eb70d293e80cd95d8ce174bb40ac011011f0c03a249"},
|
||||
{file = "tox-4.18.0.tar.gz", hash = "sha256:5dfa1cab9f146becd6e351333a82f9e0ade374451630ba65ee54584624c27b58"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "fastanime"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
description = "A browser anime site experience from the terminal"
|
||||
authors = ["Benextempest <benextempest@gmail.com>"]
|
||||
license = "UNLICENSE"
|
||||
|
||||
Reference in New Issue
Block a user