mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-02-04 11:07:48 -08:00
feat: enable use of http headers for providers
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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=None, *args):
|
||||
def SyncPlayer(url: str, anime_title=None, headers={}, *args):
|
||||
# TODO: handle m3u8 multi quality streams
|
||||
#
|
||||
# check for SyncPlay
|
||||
@@ -14,6 +14,12 @@ def SyncPlayer(url: str, anime_title=None, *args):
|
||||
exit_app(1)
|
||||
return "0", "0"
|
||||
# start SyncPlayer
|
||||
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(
|
||||
[
|
||||
@@ -23,7 +29,13 @@ def SyncPlayer(url: str, anime_title=None, *args):
|
||||
)
|
||||
else:
|
||||
subprocess.run(
|
||||
[SYNCPLAY_EXECUTABLE, url, "--", f"--force-media-title={anime_title}"]
|
||||
[
|
||||
SYNCPLAY_EXECUTABLE,
|
||||
url,
|
||||
"--",
|
||||
f"--force-media-title={anime_title}",
|
||||
*mpv_args,
|
||||
]
|
||||
)
|
||||
|
||||
# for compatability
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user