mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-01-05 01:07:13 -08:00
feat(scraper): add allanime_api based on ani-cli
This is the main api thats going to interact with the allanime site to scrape stream links. This will thus make the getting of video streams faster and more efficient than using animdl as has been previously done.
This commit is contained in:
@@ -14,6 +14,7 @@ from kivy.resources import resource_add_path, resource_find, resource_remove_pat
|
||||
from kivy.uix.screenmanager import FadeTransition, ScreenManager
|
||||
from kivy.uix.settings import Settings, SettingsWithSidebar
|
||||
from kivymd.app import MDApp
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from .libs.animdl import AnimdlApi
|
||||
from .Utility import (
|
||||
@@ -26,6 +27,7 @@ from .Utility.utils import write_crash
|
||||
from .View.components.media_card.components.media_popup import MediaPopup
|
||||
from .View.screens import screens
|
||||
|
||||
load_dotenv()
|
||||
os.environ["KIVY_VIDEO"] = "ffpyplayer" # noqa: E402
|
||||
|
||||
Config.set("graphics", "width", "1000") # noqa: E402
|
||||
@@ -428,8 +430,8 @@ def run_app():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
in_development = True
|
||||
|
||||
in_development = bool(os.environ.get("IN_DEVELOPMENT", False))
|
||||
print("In Development {}".format(in_development))
|
||||
if in_development:
|
||||
run_app()
|
||||
else:
|
||||
|
||||
289
lscraping/allanime_api.py
Normal file
289
lscraping/allanime_api.py
Normal file
@@ -0,0 +1,289 @@
|
||||
import json
|
||||
|
||||
import re
|
||||
import requests
|
||||
|
||||
# TODO: move constants to own file
|
||||
ALLANIME_BASE = "allanime.day"
|
||||
ALLANIME_REFERER = "https://allanime.to/"
|
||||
ALLANIME_API_ENDPOINT = "https://api.{}/api/".format(ALLANIME_BASE)
|
||||
|
||||
|
||||
# TODO: move th gql queries to own files
|
||||
ALLANIME_SEARCH_GQL = """
|
||||
query(
|
||||
$search: SearchInput
|
||||
$limit: Int
|
||||
$page: Int
|
||||
$translationType: VaildTranslationTypeEnumType
|
||||
$countryOrigin: VaildCountryOriginEnumType
|
||||
) {
|
||||
shows(
|
||||
search: $search
|
||||
limit: $limit
|
||||
page: $page
|
||||
translationType: $translationType
|
||||
countryOrigin: $countryOrigin
|
||||
) {
|
||||
pageInfo {
|
||||
total
|
||||
}
|
||||
edges {
|
||||
_id
|
||||
name
|
||||
availableEpisodes
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
ALLANIME_EPISODES_GQL = """\
|
||||
query ($showId: String!, $translationType: VaildTranslationTypeEnumType!, $episodeString: String!) {
|
||||
episode(
|
||||
showId: $showId
|
||||
translationType: $translationType
|
||||
episodeString: $episodeString
|
||||
) {
|
||||
|
||||
episodeString
|
||||
sourceUrls
|
||||
notes
|
||||
}
|
||||
}"""
|
||||
|
||||
ALLANIME_SHOW_GQL = """
|
||||
query ($showId: String!) {
|
||||
show(
|
||||
_id: $showId
|
||||
) {
|
||||
|
||||
_id
|
||||
name
|
||||
availableEpisodesDetail
|
||||
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# TODO: creat a utility module for this
|
||||
# Dictionary to map hex values to characters
|
||||
hex_to_char = {
|
||||
"01": "9",
|
||||
"08": "0",
|
||||
"05": "=",
|
||||
"0a": "2",
|
||||
"0b": "3",
|
||||
"0c": "4",
|
||||
"07": "?",
|
||||
"00": "8",
|
||||
"5c": "d",
|
||||
"0f": "7",
|
||||
"5e": "f",
|
||||
"17": "/",
|
||||
"54": "l",
|
||||
"09": "1",
|
||||
"48": "p",
|
||||
"4f": "w",
|
||||
"0e": "6",
|
||||
"5b": "c",
|
||||
"5d": "e",
|
||||
"0d": "5",
|
||||
"53": "k",
|
||||
"1e": "&",
|
||||
"5a": "b",
|
||||
"59": "a",
|
||||
"4a": "r",
|
||||
"4c": "t",
|
||||
"4e": "v",
|
||||
"57": "o",
|
||||
"51": "i",
|
||||
}
|
||||
|
||||
|
||||
def decode_hex_string(hex_string):
|
||||
# Split the hex string into pairs of characters
|
||||
hex_pairs = re.findall("..", hex_string)
|
||||
|
||||
# Decode each hex pair
|
||||
decoded_chars = [hex_to_char.get(pair.lower(), pair) for pair in hex_pairs]
|
||||
|
||||
return "".join(decoded_chars)
|
||||
|
||||
|
||||
# TODO: create tests for the api
|
||||
#
|
||||
class AllAnimeAPI:
|
||||
"""
|
||||
Provides a fast and effective interface to AllAnime site.
|
||||
"""
|
||||
|
||||
api_endpoint = ALLANIME_API_ENDPOINT
|
||||
|
||||
def _fetch_gql(self, query: str, variables: dict):
|
||||
response = requests.get(
|
||||
self.api_endpoint,
|
||||
params={
|
||||
"variables": json.dumps(variables),
|
||||
"query": query,
|
||||
},
|
||||
headers={
|
||||
"Referer": ALLANIME_REFERER,
|
||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0",
|
||||
},
|
||||
)
|
||||
if response.status_code != 200:
|
||||
print(response.content)
|
||||
return {}
|
||||
return response.json().get("data", {})
|
||||
|
||||
def search_for_anime(self, user_query: str, translation_type: str = "sub"):
|
||||
search = {"allowAdult": False, "allowUnknown": False, "query": user_query}
|
||||
limit = 40
|
||||
translationtype = translation_type
|
||||
countryorigin = "all"
|
||||
page = 1
|
||||
variables = {
|
||||
"search": search,
|
||||
"limit": limit,
|
||||
"page": page,
|
||||
"translationtype": translationtype,
|
||||
"countryorigin": countryorigin,
|
||||
}
|
||||
return self._fetch_gql(ALLANIME_SEARCH_GQL, variables)
|
||||
|
||||
def get_anime(self, allanime_show_id: str):
|
||||
variables = {"showId": allanime_show_id}
|
||||
return api._fetch_gql(ALLANIME_SHOW_GQL, variables)
|
||||
|
||||
def get_anime_episode(
|
||||
self, allanime_show_id: str, episode_string: str, translation_type: str = "sub"
|
||||
):
|
||||
variables = {
|
||||
"showId": allanime_show_id,
|
||||
"translationType": translation_type,
|
||||
"episodeString": episode_string,
|
||||
}
|
||||
return api._fetch_gql(ALLANIME_EPISODES_GQL, variables)
|
||||
|
||||
def get_episode_streams(self, allanime_episode_embeds_data):
|
||||
embeds = allanime_episode_embeds_data["episode"]["sourceUrls"]
|
||||
for embed in embeds:
|
||||
# filter the working streams
|
||||
if embed.get("sourceName", "") not in ("Sak", "Kir", "S-mp4", "Luf-mp4"):
|
||||
continue
|
||||
url = embed.get("sourceUrl")
|
||||
|
||||
if not url:
|
||||
continue
|
||||
if url.startswith("--"):
|
||||
url = url[2:]
|
||||
|
||||
# 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')}"
|
||||
)
|
||||
resp = requests.get(
|
||||
embed_url,
|
||||
headers={
|
||||
"Referer": ALLANIME_REFERER,
|
||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0",
|
||||
},
|
||||
)
|
||||
if resp.status_code == 200:
|
||||
match embed["sourceName"]:
|
||||
case "Luf-mp4":
|
||||
return "gogoanime", resp.json()
|
||||
case "Kir":
|
||||
return "wetransfer", resp.json()
|
||||
case "S-mp4":
|
||||
return "sharepoint", resp.json()
|
||||
case "Sak":
|
||||
return "dropbox", resp.json()
|
||||
case _:
|
||||
return "Unknown", resp.json()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
api = AllAnimeAPI()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# lets see if it works :)
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def run_fzf(options):
|
||||
"""
|
||||
Run fzf with a list of options and return the selected option.
|
||||
"""
|
||||
# Join the list of options into a single string with newlines
|
||||
options_str = "\n".join(options)
|
||||
|
||||
# Run fzf as a subprocess
|
||||
result = subprocess.run(
|
||||
["fzf"],
|
||||
input=options_str,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
# Check if fzf was successful
|
||||
if result.returncode == 0:
|
||||
# Return the selected option
|
||||
return result.stdout.strip()
|
||||
else:
|
||||
# Handle the case where fzf fails or is canceled
|
||||
print("fzf was canceled or failed")
|
||||
return None
|
||||
|
||||
anime = input("Enter the anime name: ")
|
||||
translation = input("Enter the translation type: ")
|
||||
|
||||
search_results = api.search_for_anime(anime, translation_type=translation.strip())
|
||||
if not search_results:
|
||||
raise Exception("No results found")
|
||||
|
||||
search_results = search_results["shows"]["edges"]
|
||||
options = [show["name"] for show in search_results]
|
||||
anime = run_fzf(options)
|
||||
if anime is None:
|
||||
print("No anime was selected")
|
||||
sys.exit(1)
|
||||
|
||||
anime_result = list(filter(lambda x: x["name"] == anime, search_results))[0]
|
||||
anime_data = api.get_anime(anime_result["_id"])
|
||||
|
||||
if anime_data is None:
|
||||
raise Exception("Anime not found")
|
||||
availableEpisodesDetail = anime_data["show"]["availableEpisodesDetail"]
|
||||
if not availableEpisodesDetail.get(translation.strip()):
|
||||
raise Exception("No episodes found")
|
||||
|
||||
print("select episode")
|
||||
stream_link = True
|
||||
while stream_link != "quit":
|
||||
episode = run_fzf(availableEpisodesDetail[translation.strip()])
|
||||
if episode is None:
|
||||
print("No episode was selected")
|
||||
sys.exit(1)
|
||||
|
||||
episode_data = api.get_anime_episode(anime_result["_id"], episode)
|
||||
if episode_data is None:
|
||||
raise Exception("Episode not found")
|
||||
|
||||
episode_streams = api.get_episode_streams(episode_data)
|
||||
if not episode_streams:
|
||||
raise Exception("No streams found")
|
||||
stream_links = [stream["link"] for stream in episode_streams[1]["links"]]
|
||||
stream_link = run_fzf([*stream_links, "quit"])
|
||||
|
||||
if stream_link == "quit":
|
||||
print("Have a nice day")
|
||||
sys.exit()
|
||||
subprocess.run(["mpv", stream_link])
|
||||
506
lscraping/anicli.sh
Normal file
506
lscraping/anicli.sh
Normal file
@@ -0,0 +1,506 @@
|
||||
#!/bin/sh
|
||||
|
||||
version_number="4.8.8"
|
||||
|
||||
# UI
|
||||
|
||||
external_menu() {
|
||||
rofi "$1" -sort -dmenu -i -width 1500 -p "$2"
|
||||
}
|
||||
|
||||
launcher() {
|
||||
[ "$use_external_menu" = "0" ] && [ -z "$1" ] && set -- "+m" "$2"
|
||||
[ "$use_external_menu" = "0" ] && fzf "$1" --reverse --cycle --prompt "$2"
|
||||
[ "$use_external_menu" = "1" ] && external_menu "$1" "$2"
|
||||
}
|
||||
|
||||
nth() {
|
||||
stdin=$(cat -)
|
||||
[ -z "$stdin" ] && return 1
|
||||
line_count="$(printf "%s\n" "$stdin" | wc -l | tr -d "[:space:]")"
|
||||
[ "$line_count" -eq 1 ] && printf "%s" "$stdin" | cut -f2,3 && return 0
|
||||
prompt="$1"
|
||||
multi_flag=""
|
||||
[ $# -ne 1 ] && shift && multi_flag="$1"
|
||||
line=$(printf "%s" "$stdin" | cut -f1,3 | tr '\t' ' ' | launcher "$multi_flag" "$prompt" | cut -d " " -f 1)
|
||||
[ -n "$line" ] && printf "%s" "$stdin" | grep -E '^'"${line}"'($|[[:space:]])' | cut -f2,3 || exit 1
|
||||
}
|
||||
|
||||
die() {
|
||||
printf "\33[2K\r\033[1;31m%s\033[0m\n" "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
help_info() {
|
||||
printf "
|
||||
Usage:
|
||||
%s [options] [query]
|
||||
%s [query] [options]
|
||||
%s [options] [query] [options]
|
||||
|
||||
Options:
|
||||
-c, --continue
|
||||
Continue watching from history
|
||||
-d, --download
|
||||
Download the video instead of playing it
|
||||
-D, --delete
|
||||
Delete history
|
||||
-s, --syncplay
|
||||
Use Syncplay to watch with friends
|
||||
-S, --select-nth
|
||||
Select nth entry
|
||||
-q, --quality
|
||||
Specify the video quality
|
||||
-v, --vlc
|
||||
Use VLC to play the video
|
||||
-V, --version
|
||||
Show the version of the script
|
||||
-h, --help
|
||||
Show this help message and exit
|
||||
-e, --episode, -r, --range
|
||||
Specify the number of episodes to watch
|
||||
--dub
|
||||
Play dubbed version
|
||||
--rofi
|
||||
Use rofi instead of fzf for the interactive menu
|
||||
--skip
|
||||
Use ani-skip to skip the intro of the episode (mpv only)
|
||||
--no-detach
|
||||
Don't detach the player (useful for in-terminal playback, mpv only)
|
||||
--exit-after-play
|
||||
Exit the player, and return the player exit code (useful for non interactive scenarios, works only if --no-detach is used, mpv only)
|
||||
--skip-title <title>
|
||||
Use given title as ani-skip query
|
||||
-N, --nextep-countdown
|
||||
Display a countdown to the next episode
|
||||
-U, --update
|
||||
Update the script
|
||||
Some example usages:
|
||||
%s -q 720p banana fish
|
||||
%s --skip --skip-title \"one piece\" -S 2 one piece
|
||||
%s -d -e 2 cyberpunk edgerunners
|
||||
%s --vlc cyberpunk edgerunners -q 1080p -e 4
|
||||
%s blue lock -e 5-6
|
||||
%s -e \"5 6\" blue lock
|
||||
\n" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}"
|
||||
exit 0
|
||||
}
|
||||
|
||||
version_info() {
|
||||
printf "%s\n" "$version_number"
|
||||
exit 0
|
||||
}
|
||||
|
||||
update_script() {
|
||||
update="$(curl -s -A "$agent" "https://raw.githubusercontent.com/pystardust/ani-cli/master/ani-cli")" || die "Connection error"
|
||||
update="$(printf '%s\n' "$update" | diff -u "$0" -)"
|
||||
if [ -z "$update" ]; then
|
||||
printf "Script is up to date :)\n"
|
||||
else
|
||||
if printf '%s\n' "$update" | patch "$0" -; then
|
||||
printf "Script has been updated\n"
|
||||
else
|
||||
die "Can't update for some reason!"
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
# checks if dependencies are present
|
||||
dep_ch() {
|
||||
for dep; do
|
||||
command -v "$dep" >/dev/null || die "Program \"$dep\" not found. Please install it."
|
||||
done
|
||||
}
|
||||
|
||||
# SCRAPING
|
||||
|
||||
# extract the video links from response of embed urls, extract mp4 links form m3u8 lists
|
||||
get_links() {
|
||||
episode_link="$(curl -e "$allanime_refr" -s "https://${allanime_base}$*" -A "$agent" | sed 's|},{|\
|
||||
|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p;s|.*hls","url":"([^"]*)".*"hardsub_lang":"en-US".*|\1|p')"
|
||||
|
||||
case "$episode_link" in
|
||||
*vipanicdn* | *anifastcdn*)
|
||||
if printf "%s" "$episode_link" | head -1 | grep -q "original.m3u"; then
|
||||
printf "%s" "$episode_link"
|
||||
else
|
||||
extract_link=$(printf "%s" "$episode_link" | head -1 | cut -d'>' -f2)
|
||||
relative_link=$(printf "%s" "$extract_link" | sed 's|[^/]*$||')
|
||||
curl -e "$allanime_refr" -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\
|
||||
| >|' | sed "s|>|>${relative_link}|g" | sort -nr
|
||||
fi
|
||||
;;
|
||||
*) [ -n "$episode_link" ] && printf "%s\n" "$episode_link" ;;
|
||||
esac
|
||||
[ -z "$ANI_CLI_NON_INTERACTIVE" ] && printf "\033[1;32m%s\033[0m Links Fetched\n" "$provider_name" 1>&2
|
||||
}
|
||||
|
||||
# innitialises provider_name and provider_id. First argument is the provider name, 2nd is the regex that matches that provider's link
|
||||
provider_init() {
|
||||
provider_name=$1
|
||||
provider_id=$(printf "%s" "$resp" | sed -n "$2" | head -1 | cut -d':' -f2 | sed 's/../&\
|
||||
/g' | sed 's/^01$/9/g;s/^08$/0/g;s/^05$/=/g;s/^0a$/2/g;s/^0b$/3/g;s/^0c$/4/g;s/^07$/?/g;s/^00$/8/g;s/^5c$/d/g;s/^0f$/7/g;s/^5e$/f/g;s/^17$/\//g;s/^54$/l/g;s/^09$/1/g;s/^48$/p/g;s/^4f$/w/g;s/^0e$/6/g;s/^5b$/c/g;s/^5d$/e/g;s/^0d$/5/g;s/^53$/k/g;s/^1e$/\&/g;s/^5a$/b/g;s/^59$/a/g;s/^4a$/r/g;s/^4c$/t/g;s/^4e$/v/g;s/^57$/o/g;s/^51$/i/g;' | tr -d '\n' | sed "s/\/clock/\/clock\.json/")
|
||||
}
|
||||
|
||||
# generates links based on given provider
|
||||
generate_link() {
|
||||
case $1 in
|
||||
1) provider_init "dropbox" "/Sak :/p" ;; # dropbox(mp4)(single)
|
||||
2) provider_init "wetransfer" "/Kir :/p" ;; # wetransfer(mp4)(single)
|
||||
3) provider_init "sharepoint" "/S-mp4 :/p" ;; # sharepoint(mp4)(single)
|
||||
*) provider_init "gogoanime" "/Luf-mp4 :/p" ;; # gogoanime(m3u8)(multi)
|
||||
esac
|
||||
[ -n "$provider_id" ] && get_links "$provider_id"
|
||||
}
|
||||
|
||||
select_quality() {
|
||||
case "$1" in
|
||||
best) result=$(printf "%s" "$links" | head -n1) ;;
|
||||
worst) result=$(printf "%s" "$links" | grep -E '^[0-9]{3,4}' | tail -n1) ;;
|
||||
*) result=$(printf "%s" "$links" | grep -m 1 "$1") ;;
|
||||
esac
|
||||
[ -z "$result" ] && printf "Specified quality not found, defaulting to best\n" 1>&2 && result=$(printf "%s" "$links" | head -n1)
|
||||
printf "%s" "$result" | cut -d'>' -f2
|
||||
}
|
||||
|
||||
# gets embed urls, collects direct links into provider files, selects one with desired quality into $episode
|
||||
get_episode_url() {
|
||||
# get the embed urls of the selected episode
|
||||
episode_embed_gql="query (\$showId: String!, \$translationType: VaildTranslationTypeEnumType!, \$episodeString: String!) { episode( showId: \$showId translationType: \$translationType episodeString: \$episodeString ) { episodeString sourceUrls }}"
|
||||
|
||||
resp=$(curl -e "$allanime_refr" -s -G "${allanime_api}/api" --data-urlencode "variables={\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"}" --data-urlencode "query=$episode_embed_gql" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":"--([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p')
|
||||
# generate links into sequential files
|
||||
cache_dir="$(mktemp -d)"
|
||||
providers="1 2 3 4"
|
||||
for provider in $providers; do
|
||||
generate_link "$provider" >"$cache_dir"/"$provider" &
|
||||
done
|
||||
wait
|
||||
# select the link with matching quality
|
||||
links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g;/http/!d' | sort -g -r -s)
|
||||
rm -r "$cache_dir"
|
||||
episode=$(select_quality "$quality")
|
||||
[ -z "$episode" ] && die "Episode not released!"
|
||||
}
|
||||
|
||||
# search the query and give results
|
||||
search_anime() {
|
||||
search_gql="query( \$search: SearchInput \$limit: Int \$page: Int \$translationType: VaildTranslationTypeEnumType \$countryOrigin: VaildCountryOriginEnumType ) { shows( search: \$search limit: \$limit page: \$page translationType: \$translationType countryOrigin: \$countryOrigin ) { edges { _id name availableEpisodes __typename } }}"
|
||||
|
||||
curl -e "$allanime_refr" -s -G "${allanime_api}/api" --data-urlencode "variables={\"search\":{\"allowAdult\":false,\"allowUnknown\":false,\"query\":\"$1\"},\"limit\":40,\"page\":1,\"translationType\":\"$mode\",\"countryOrigin\":\"ALL\"}" --data-urlencode "query=$search_gql" -A "$agent" | sed 's|Show|\
|
||||
|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*${mode}\":([1-9][^,]*).*|\1 \2 (\3 episodes)|p"
|
||||
}
|
||||
|
||||
time_until_next_ep() {
|
||||
animeschedule="https://animeschedule.net"
|
||||
curl -s -G "$animeschedule/api/v3/anime" --data-urlencode "q=$1" | sed 's|"id"|\n|g' | sed -nE 's|.*,"route":"([^"]*)","premier.*|\1|p' | while read -r anime; do
|
||||
data=$(curl -s "$animeschedule/anime/$anime" | sed '1,/"anime-header-list-buttons-wrapper"/d' | sed -nE 's|.*countdown-time-raw" datetime="([^"]*)">.*|Next Raw Release: \1|p;s|.*countdown-time" datetime="([^"]*)">.*|Next Sub Release: \1|p;s|.*english-title">([^<]*)<.*|English Title: \1|p;s|.*main-title".*>([^<]*)<.*|Japanese Title: \1|p')
|
||||
status="Ongoing"
|
||||
color="33"
|
||||
printf "%s\n" "$data"
|
||||
! (printf "%s\n" "$data" | grep -q "Next Raw Release:") && status="Finished" && color="32"
|
||||
printf "Status: \033[1;%sm%s\033[0m\n---\n" "$color" "$status"
|
||||
done
|
||||
exit 0
|
||||
}
|
||||
|
||||
# get the episodes list of the selected anime
|
||||
episodes_list() {
|
||||
episodes_list_gql="query (\$showId: String!) { show( _id: \$showId ) { _id availableEpisodesDetail }}"
|
||||
|
||||
curl -e "$allanime_refr" -s -G "${allanime_api}/api" --data-urlencode "variables={\"showId\":\"$*\"}" --data-urlencode "query=$episodes_list_gql" -A "$agent" | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\
|
||||
|g; s|"||g' | sort -n -k 1
|
||||
}
|
||||
|
||||
# PLAYING
|
||||
|
||||
process_hist_entry() {
|
||||
ep_list=$(episodes_list "$id")
|
||||
ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null
|
||||
[ -n "$ep_no" ] && printf "%s\t%s - episode %s\n" "$id" "$title" "$ep_no"
|
||||
}
|
||||
|
||||
update_history() {
|
||||
if grep -q -- "$id" "$histfile"; then
|
||||
sed -E "s/^[^\t]+\t${id}\t/${ep_no}\t${id}\t/" "$histfile" >"${histfile}.new"
|
||||
else
|
||||
cp "$histfile" "${histfile}.new"
|
||||
printf "%s\t%s\t%s\n" "$ep_no" "$id" "$title" >>"${histfile}.new"
|
||||
fi
|
||||
mv "${histfile}.new" "$histfile"
|
||||
}
|
||||
|
||||
download() {
|
||||
case $1 in
|
||||
*m3u8*)
|
||||
if command -v "yt-dlp" >/dev/null; then
|
||||
yt-dlp "$1" --no-skip-unavailable-fragments --fragment-retries infinite -N 16 -o "$download_dir/$2.mp4"
|
||||
else
|
||||
ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
aria2c --enable-rpc=false --check-certificate=false --continue --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
play_episode() {
|
||||
[ "$skip_intro" = 1 ] && skip_flag="$(ani-skip -q "$mal_id" -e "$ep_no")"
|
||||
[ -z "$episode" ] && get_episode_url
|
||||
# shellcheck disable=SC2086
|
||||
case "$player_function" in
|
||||
debug)
|
||||
[ -z "$ANI_CLI_NON_INTERACTIVE" ] && printf "All links:\n%s\nSelected link:\n" "$links"
|
||||
printf "%s\n" "$episode"
|
||||
;;
|
||||
mpv*)
|
||||
if [ "$no_detach" = 0 ]; then
|
||||
nohup "$player_function" $skip_flag --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" >/dev/null 2>&1 &
|
||||
else
|
||||
"$player_function" $skip_flag --force-media-title="${allanime_title}Episode ${ep_no}" "$episode"
|
||||
mpv_exitcode=$?
|
||||
[ "$exit_after_play" = 1 ] && exit "$mpv_exitcode"
|
||||
fi
|
||||
;;
|
||||
android_mpv) nohup am start --user 0 -a android.intent.action.VIEW -d "$episode" -n is.xyz.mpv/.MPVActivity >/dev/null 2>&1 & ;;
|
||||
android_vlc) nohup am start --user 0 -a android.intent.action.VIEW -d "$episode" -n org.videolan.vlc/org.videolan.vlc.gui.video.VideoPlayerActivity -e "title" "${allanime_title}Episode ${ep_no}" >/dev/null 2>&1 & ;;
|
||||
iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}Episode ${ep_no}" "$episode" >/dev/null 2>&1 & ;;
|
||||
flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" >/dev/null 2>&1 & ;;
|
||||
vlc*) nohup "$player_function" --play-and-exit --meta-title="${allanime_title}Episode ${ep_no}" "$episode" >/dev/null 2>&1 & ;;
|
||||
*yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}Episode ${ep_no}" >/dev/null 2>&1 & ;;
|
||||
download) "$player_function" "$episode" "${allanime_title}Episode ${ep_no}" ;;
|
||||
catt) nohup catt cast "$episode" >/dev/null 2>&1 & ;;
|
||||
iSH)
|
||||
printf "\e]8;;vlc://%s\a~~~~~~~~~~~~~~~~~~~~\n~ Tap to open VLC ~\n~~~~~~~~~~~~~~~~~~~~\e]8;;\a\n" "$episode"
|
||||
sleep 5
|
||||
;;
|
||||
*) nohup "$player_function" "$episode" >/dev/null 2>&1 & ;;
|
||||
esac
|
||||
replay="$episode"
|
||||
unset episode
|
||||
update_history
|
||||
[ "$use_external_menu" = "1" ] && wait
|
||||
}
|
||||
|
||||
play() {
|
||||
start=$(printf "%s" "$ep_no" | grep -Eo '^(-1|[0-9]+(\.[0-9]+)?)')
|
||||
end=$(printf "%s" "$ep_no" | grep -Eo '(-1|[0-9]+(\.[0-9]+)?)$')
|
||||
[ "$start" = "-1" ] && ep_no=$(printf "%s" "$ep_list" | tail -n1) && unset start
|
||||
[ -z "$end" ] || [ "$end" = "$start" ] && unset start end
|
||||
[ "$end" = "-1" ] && end=$(printf "%s" "$ep_list" | tail -n1)
|
||||
line_count=$(printf "%s\n" "$ep_no" | wc -l | tr -d "[:space:]")
|
||||
if [ "$line_count" != 1 ] || [ -n "$start" ]; then
|
||||
[ -z "$start" ] && start=$(printf "%s\n" "$ep_no" | head -n1)
|
||||
[ -z "$end" ] && end=$(printf "%s\n" "$ep_no" | tail -n1)
|
||||
range=$(printf "%s\n" "$ep_list" | sed -nE "/^${start}\$/,/^${end}\$/p")
|
||||
[ -z "$range" ] && die "Invalid range!"
|
||||
for i in $range; do
|
||||
tput clear
|
||||
ep_no=$i
|
||||
printf "\33[2K\r\033[1;34mPlaying episode %s...\033[0m\n" "$ep_no"
|
||||
play_episode
|
||||
done
|
||||
else
|
||||
play_episode
|
||||
fi
|
||||
# moves upto stored positon and deletes to end
|
||||
[ "$player_function" != "debug" ] && [ "$player_function" != "download" ] && tput rc && tput ed
|
||||
}
|
||||
|
||||
# MAIN
|
||||
|
||||
# setup
|
||||
agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0"
|
||||
allanime_refr="https://allanime.to"
|
||||
allanime_base="allanime.day"
|
||||
allanime_api="https://api.${allanime_base}"
|
||||
mode="${ANI_CLI_MODE:-sub}"
|
||||
download_dir="${ANI_CLI_DOWNLOAD_DIR:-.}"
|
||||
quality="${ANI_CLI_QUALITY:-best}"
|
||||
case "$(uname -a)" in
|
||||
*Darwin*) player_function="${ANI_CLI_PLAYER:-iina}" ;; # mac OS
|
||||
*ndroid*) player_function="${ANI_CLI_PLAYER:-android_mpv}" ;; # Android OS (termux)
|
||||
*steamdeck*) player_function="${ANI_CLI_PLAYER:-flatpak_mpv}" ;; # steamdeck OS
|
||||
*MINGW* | *WSL2*) player_function="${ANI_CLI_PLAYER:-mpv.exe}" ;; # Windows OS
|
||||
*ish*) player_function="${ANI_CLI_PLAYER:-iSH}" ;; # iOS (iSH)
|
||||
*) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS
|
||||
esac
|
||||
|
||||
no_detach="${ANI_CLI_NO_DETACH:-0}"
|
||||
exit_after_play="${ANI_CLI_EXIT_AFTER_PLAY:-0}"
|
||||
use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}"
|
||||
skip_intro="${ANI_CLI_SKIP_INTRO:-0}"
|
||||
# shellcheck disable=SC2154
|
||||
skip_title="$ANI_CLI_SKIP_TITLE"
|
||||
[ -t 0 ] || use_external_menu=1
|
||||
hist_dir="${ANI_CLI_HIST_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli}"
|
||||
[ ! -d "$hist_dir" ] && mkdir -p "$hist_dir"
|
||||
histfile="$hist_dir/ani-hsts"
|
||||
[ ! -f "$histfile" ] && : >"$histfile"
|
||||
search="${ANI_CLI_DEFAULT_SOURCE:-scrape}"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-v | --vlc)
|
||||
case "$(uname -a)" in
|
||||
*ndroid*) player_function="android_vlc" ;;
|
||||
MINGW* | *WSL2*) player_function="vlc.exe" ;;
|
||||
*ish*) player_function="iSH" ;;
|
||||
*) player_function="vlc" ;;
|
||||
esac
|
||||
;;
|
||||
-s | --syncplay)
|
||||
case "$(uname -s)" in
|
||||
Darwin*) player_function="/Applications/Syncplay.app/Contents/MacOS/syncplay" ;;
|
||||
MINGW* | *Msys)
|
||||
export PATH="$PATH":"/c/Program Files (x86)/Syncplay/"
|
||||
player_function="syncplay.exe"
|
||||
;;
|
||||
*) player_function="syncplay" ;;
|
||||
esac
|
||||
;;
|
||||
-q | --quality)
|
||||
[ $# -lt 2 ] && die "missing argument!"
|
||||
quality="$2"
|
||||
shift
|
||||
;;
|
||||
-S | --select-nth)
|
||||
[ $# -lt 2 ] && die "missing argument!"
|
||||
index="$2"
|
||||
shift
|
||||
;;
|
||||
-c | --continue) search=history ;;
|
||||
-d | --download) player_function=download ;;
|
||||
-D | --delete)
|
||||
: >"$histfile"
|
||||
exit 0
|
||||
;;
|
||||
-V | --version) version_info ;;
|
||||
-h | --help) help_info ;;
|
||||
-e | --episode | -r | --range)
|
||||
[ $# -lt 2 ] && die "missing argument!"
|
||||
ep_no="$2"
|
||||
[ -n "$index" ] && ANI_CLI_NON_INTERACTIVE=1 #Checks for -S presence
|
||||
shift
|
||||
;;
|
||||
--dub) mode="dub" ;;
|
||||
--no-detach) no_detach=1 ;;
|
||||
--exit-after-play) exit_after_play=1 ;;
|
||||
--rofi) use_external_menu=1 ;;
|
||||
--skip) skip_intro=1 ;;
|
||||
--skip-title)
|
||||
[ $# -lt 2 ] && die "missing argument!"
|
||||
skip_title="$2"
|
||||
shift
|
||||
;;
|
||||
-N | --nextep-countdown) search=nextep ;;
|
||||
-U | --update) update_script ;;
|
||||
*) query="$(printf "%s" "$query $1" | sed "s|^ ||;s| |+|g")" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
[ "$use_external_menu" = "0" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-"-m"}"
|
||||
[ "$use_external_menu" = "1" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-"-multi-select"}"
|
||||
printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n"
|
||||
dep_ch "curl" "sed" "grep" || true
|
||||
[ "$skip_intro" = 1 ] && (dep_ch "ani-skip" || true)
|
||||
if [ -z "$ANI_CLI_NON_INTERACTIVE" ]; then dep_ch fzf || true; fi
|
||||
case "$player_function" in
|
||||
debug) ;;
|
||||
download) dep_ch "ffmpeg" "aria2c" ;;
|
||||
flatpak*)
|
||||
dep_ch "flatpak"
|
||||
flatpak info io.mpv.Mpv >/dev/null 2>&1 || die "Program \"mpv (flatpak)\" not found. Please install it."
|
||||
;;
|
||||
android*) printf "\33[2K\rChecking of players on Android is disabled\n" ;;
|
||||
*iSH*) printf "\33[2K\rChecking of players on iOS is disabled\n" ;;
|
||||
*) dep_ch "$player_function" ;;
|
||||
esac
|
||||
|
||||
# searching
|
||||
case "$search" in
|
||||
history)
|
||||
anime_list=$(while read -r ep_no id title; do process_hist_entry & done <"$histfile")
|
||||
wait
|
||||
[ -z "$anime_list" ] && die "No unwatched series in history!"
|
||||
result=$(printf "%s" "$anime_list" | nl -w 2 | sed 's/^[[:space:]]//' | nth "Select anime: " | cut -f1)
|
||||
[ -z "$result" ] && exit 1
|
||||
resfile="$(mktemp)"
|
||||
grep "$result" "$histfile" >"$resfile"
|
||||
read -r ep_no id title <"$resfile"
|
||||
ep_list=$(episodes_list "$id")
|
||||
ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null
|
||||
allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]')"
|
||||
;;
|
||||
*)
|
||||
if [ "$use_external_menu" = "0" ]; then
|
||||
while [ -z "$query" ]; do
|
||||
printf "\33[2K\r\033[1;36mSearch anime: \033[0m" && read -r query
|
||||
done
|
||||
else
|
||||
[ -z "$query" ] && query=$(printf "" | external_menu "" "Search anime: ")
|
||||
[ -z "$query" ] && exit 1
|
||||
fi
|
||||
# for checking new releases by specifying anime name
|
||||
[ "$search" = "nextep" ] && time_until_next_ep "$query"
|
||||
|
||||
query=$(printf "%s" "$query" | sed "s| |+|g")
|
||||
anime_list=$(search_anime "$query")
|
||||
[ -z "$anime_list" ] && die "No results found!"
|
||||
[ "$index" -eq "$index" ] 2>/dev/null && result=$(printf "%s" "$anime_list" | sed -n "${index}p")
|
||||
[ -z "$index" ] && result=$(printf "%s" "$anime_list" | nl -w 2 | sed 's/^[[:space:]]//' | nth "Select anime: ")
|
||||
[ -z "$result" ] && exit 1
|
||||
title=$(printf "%s" "$result" | cut -f2)
|
||||
allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]')"
|
||||
id=$(printf "%s" "$result" | cut -f1)
|
||||
ep_list=$(episodes_list "$id")
|
||||
[ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$multi_selection_flag")
|
||||
[ -z "$ep_no" ] && exit 1
|
||||
;;
|
||||
esac
|
||||
[ "$skip_intro" = 1 ] && mal_id="$(ani-skip -q "${skip_title:-${title}}")"
|
||||
|
||||
# moves the cursor up one line and clears that line
|
||||
tput cuu1 && tput el
|
||||
# stores the positon of cursor
|
||||
tput sc
|
||||
|
||||
# playback & loop
|
||||
play
|
||||
[ "$player_function" = "download" ] || [ "$player_function" = "debug" ] && exit 0
|
||||
|
||||
while cmd=$(printf "next\nreplay\nprevious\nselect\nchange_quality\nquit" | nth "Playing episode $ep_no of $title... "); do
|
||||
case "$cmd" in
|
||||
next) ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null ;;
|
||||
replay) episode="$replay" ;;
|
||||
previous) ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{g;1!p;};h") 2>/dev/null ;;
|
||||
select) ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$multi_selection_flag") ;;
|
||||
change_quality)
|
||||
episode=$(printf "%s" "$links" | launcher)
|
||||
quality=$(printf "%s" "$episode" | grep -oE "^[0-9]+")
|
||||
episode=$(printf "%s" "$episode" | cut -d'>' -f2)
|
||||
;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
[ -z "$ep_no" ] && die "Out of range"
|
||||
play
|
||||
done
|
||||
|
||||
# ani-cli
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Project repository: https://github.com/pystardust/ani-cli
|
||||
209
lscraping/data.json
Normal file
209
lscraping/data.json
Normal file
@@ -0,0 +1,209 @@
|
||||
{
|
||||
"shows": {
|
||||
"pageInfo": {
|
||||
"total": null
|
||||
},
|
||||
"edges": [
|
||||
{
|
||||
"_id": "GoSNgenDWLm2B8C3H",
|
||||
"name": "Highspeed Etoile",
|
||||
"availableEpisodes": {
|
||||
"sub": 10,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "8Suk8xq3YmvDq2s5C",
|
||||
"name": "Girls Band Cry",
|
||||
"availableEpisodes": {
|
||||
"sub": 10,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "rwroGT45PMJiWkeYY",
|
||||
"name": "The iDOLM@STER Shiny Colors",
|
||||
"availableEpisodes": {
|
||||
"sub": 10,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "pNPGCMYN5yA4yQH5J",
|
||||
"name": "Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou 2nd Season Part 2",
|
||||
"availableEpisodes": {
|
||||
"sub": 8,
|
||||
"dub": 2,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "Q7h4rxYmdHDADCTfY",
|
||||
"name": "Mahouka Koukou no Rettousei Season 3",
|
||||
"availableEpisodes": {
|
||||
"sub": 10,
|
||||
"dub": 3,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "KB5XDvwPdtLFEkoQZ",
|
||||
"name": "Tensei shitara Slime Datta Ken Season 3",
|
||||
"availableEpisodes": {
|
||||
"sub": 11,
|
||||
"dub": 8,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "QvmdS433kzi6gy8EQ",
|
||||
"name": "Astro Note",
|
||||
"availableEpisodes": {
|
||||
"sub": 10,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "A3YuyeYdar4cPvix2",
|
||||
"name": "Nyaaaanvy",
|
||||
"availableEpisodes": {
|
||||
"sub": 2,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "vHRnxWT83vEhRzcAG",
|
||||
"name": "Nijiyon Animation 2",
|
||||
"availableEpisodes": {
|
||||
"sub": 10,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "ZDxMfy9mmxEbjryKP",
|
||||
"name": "Ni Tian Zhizun",
|
||||
"availableEpisodes": {
|
||||
"sub": 308,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "ox2opWHHwh2d663NG",
|
||||
"name": "Ling Jian Zun Season 4",
|
||||
"availableEpisodes": {
|
||||
"sub": 394,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "wcGJw5u7NRiabB4qz",
|
||||
"name": "Exclusive Possession: Young Master Ji's Beloved Wife Season 3",
|
||||
"availableEpisodes": {
|
||||
"sub": 59,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "M5Z6D7DXveoMSKXN7",
|
||||
"name": "Bai Lian Cheng Shen Season 2",
|
||||
"availableEpisodes": {
|
||||
"sub": 28,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "6oDA6BDxtLCJq5FZx",
|
||||
"name": "Wanmei Shijie",
|
||||
"availableEpisodes": {
|
||||
"sub": 166,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "5hpioXkSfCNYX4k5M",
|
||||
"name": "Da Zhu Zai Nian Fan",
|
||||
"availableEpisodes": {
|
||||
"sub": 52,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "FDbFjEnbDTbrGJ757",
|
||||
"name": "Yuan Zun (2024)",
|
||||
"availableEpisodes": {
|
||||
"sub": 4,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "2z6h3kbiB4xSafQcj",
|
||||
"name": "Dead Dead Demons Dededede Destruction",
|
||||
"availableEpisodes": {
|
||||
"sub": 3,
|
||||
"dub": 3,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "gYNWJBu4rvSrmSfiB",
|
||||
"name": "Henjin no Salad Bowl",
|
||||
"availableEpisodes": {
|
||||
"sub": 10,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "eDZ9AzEzXu3LQ3uTK",
|
||||
"name": "Muu no Hakugei",
|
||||
"availableEpisodes": {
|
||||
"sub": 2,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "dBjYbRzRyWg59E57c",
|
||||
"name": "Chiikawa",
|
||||
"availableEpisodes": {
|
||||
"sub": 95,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
1
lscraping/data2.json
Normal file
1
lscraping/data2.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shows": {"pageInfo": {"total": null}, "edges": [{"_id": "GoSNgenDWLm2B8C3H", "name": "Highspeed Etoile", "availableEpisodes": {"sub": 10, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "8Suk8xq3YmvDq2s5C", "name": "Girls Band Cry", "availableEpisodes": {"sub": 10, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "rwroGT45PMJiWkeYY", "name": "The iDOLM@STER Shiny Colors", "availableEpisodes": {"sub": 10, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "pNPGCMYN5yA4yQH5J", "name": "Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou 2nd Season Part 2", "availableEpisodes": {"sub": 8, "dub": 2, "raw": 0}, "__typename": "Show"}, {"_id": "Q7h4rxYmdHDADCTfY", "name": "Mahouka Koukou no Rettousei Season 3", "availableEpisodes": {"sub": 10, "dub": 3, "raw": 0}, "__typename": "Show"}, {"_id": "KB5XDvwPdtLFEkoQZ", "name": "Tensei shitara Slime Datta Ken Season 3", "availableEpisodes": {"sub": 11, "dub": 8, "raw": 0}, "__typename": "Show"}, {"_id": "QvmdS433kzi6gy8EQ", "name": "Astro Note", "availableEpisodes": {"sub": 10, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "A3YuyeYdar4cPvix2", "name": "Nyaaaanvy", "availableEpisodes": {"sub": 2, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "vHRnxWT83vEhRzcAG", "name": "Nijiyon Animation 2", "availableEpisodes": {"sub": 10, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "ZDxMfy9mmxEbjryKP", "name": "Ni Tian Zhizun", "availableEpisodes": {"sub": 308, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "ox2opWHHwh2d663NG", "name": "Ling Jian Zun Season 4", "availableEpisodes": {"sub": 394, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "wcGJw5u7NRiabB4qz", "name": "Exclusive Possession: Young Master Ji's Beloved Wife Season 3", "availableEpisodes": {"sub": 59, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "M5Z6D7DXveoMSKXN7", "name": "Bai Lian Cheng Shen Season 2", "availableEpisodes": {"sub": 28, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "6oDA6BDxtLCJq5FZx", "name": "Wanmei Shijie", "availableEpisodes": {"sub": 166, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "5hpioXkSfCNYX4k5M", "name": "Da Zhu Zai Nian Fan", "availableEpisodes": {"sub": 52, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "FDbFjEnbDTbrGJ757", "name": "Yuan Zun (2024)", "availableEpisodes": {"sub": 4, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "2z6h3kbiB4xSafQcj", "name": "Dead Dead Demons Dededede Destruction", "availableEpisodes": {"sub": 3, "dub": 3, "raw": 0}, "__typename": "Show"}, {"_id": "gYNWJBu4rvSrmSfiB", "name": "Henjin no Salad Bowl", "availableEpisodes": {"sub": 10, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "eDZ9AzEzXu3LQ3uTK", "name": "Muu no Hakugei", "availableEpisodes": {"sub": 2, "dub": 0, "raw": 0}, "__typename": "Show"}, {"_id": "dBjYbRzRyWg59E57c", "name": "Chiikawa", "availableEpisodes": {"sub": 95, "dub": 0, "raw": 0}, "__typename": "Show"}]}}
|
||||
159
lscraping/data3.json
Normal file
159
lscraping/data3.json
Normal file
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"shows": {
|
||||
"pageInfo": {
|
||||
"total": 22279
|
||||
},
|
||||
"edges": [
|
||||
{
|
||||
"_id": "JsbMBPoTsp3RkGfrf",
|
||||
"name": "Sword Art Online: Progressive Movie: Kuraki Yuuyami no Scherzo",
|
||||
"availableEpisodes": {
|
||||
"sub": 1,
|
||||
"dub": 1,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "YXEMgd5FLhWD2mn8Z",
|
||||
"name": "Sword Art Online: Progressive Movie - Hoshi Naki Yoru no Aria",
|
||||
"availableEpisodes": {
|
||||
"sub": 1,
|
||||
"dub": 1,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "ALj8oEW6mhWuQ8pHT",
|
||||
"name": "Sword Art Online Movie: Ordinal Scale: Sword Art Offline",
|
||||
"availableEpisodes": {
|
||||
"sub": 1,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "DWZeL5vDhEokJQFXR",
|
||||
"name": "Sword Art Offline II",
|
||||
"availableEpisodes": {
|
||||
"sub": 9,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "fgEBgLfkzEWaCSHAu",
|
||||
"name": "Sword Art Online: Sword Art Offline",
|
||||
"availableEpisodes": {
|
||||
"sub": 9,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "BvzJ7btmDGBvJCC4P",
|
||||
"name": "Sword Art Online: Extra Edition",
|
||||
"availableEpisodes": {
|
||||
"sub": 1,
|
||||
"dub": 1,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "4mjsuWL9sAK97zQjL",
|
||||
"name": "Sword Art Offline: Extra Edition",
|
||||
"availableEpisodes": {
|
||||
"sub": 1,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "np7xM323v5pH6KY9e",
|
||||
"name": "Sword Art Online: Alicization",
|
||||
"availableEpisodes": {
|
||||
"sub": 25,
|
||||
"dub": 25,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "36bqKyjS3tFybyz7j",
|
||||
"name": "Sword Art Online",
|
||||
"availableEpisodes": {
|
||||
"sub": 25,
|
||||
"dub": 25,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "ierXJrDYNiALG5haP",
|
||||
"name": "Sword Art Online II",
|
||||
"availableEpisodes": {
|
||||
"sub": 26,
|
||||
"dub": 25,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "tkywv9gMqH7mYw4N5",
|
||||
"name": "Sword Art Online: Alicization - War of Underworld Season 2",
|
||||
"availableEpisodes": {
|
||||
"sub": 11,
|
||||
"dub": 11,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "z2stShodurTBWbXgM",
|
||||
"name": "Sword Art Online: Alicization - War of Underworld",
|
||||
"availableEpisodes": {
|
||||
"sub": 14,
|
||||
"dub": 12,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "WwdepehfXq8gpgku9",
|
||||
"name": "Sword Art Online Alternative: Gun Gale Online",
|
||||
"availableEpisodes": {
|
||||
"sub": 13,
|
||||
"dub": 12,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "hjHnDp4NRh9zpcTLJ",
|
||||
"name": "Sword Art Online: Ordinal Scale",
|
||||
"availableEpisodes": {
|
||||
"sub": 1,
|
||||
"dub": 1,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
},
|
||||
{
|
||||
"_id": "TYTW9KtABBNiMcp5w",
|
||||
"name": "Sword Art Online II: Debriefing",
|
||||
"availableEpisodes": {
|
||||
"sub": 1,
|
||||
"dub": 0,
|
||||
"raw": 0
|
||||
},
|
||||
"__typename": "Show"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
52
lscraping/f.py
Normal file
52
lscraping/f.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import re
|
||||
|
||||
|
||||
# Dictionary to map hex values to characters
|
||||
hex_to_char = {
|
||||
"01": "9",
|
||||
"08": "0",
|
||||
"05": "=",
|
||||
"0a": "2",
|
||||
"0b": "3",
|
||||
"0c": "4",
|
||||
"07": "?",
|
||||
"00": "8",
|
||||
"5c": "d",
|
||||
"0f": "7",
|
||||
"5e": "f",
|
||||
"17": "/",
|
||||
"54": "l",
|
||||
"09": "1",
|
||||
"48": "p",
|
||||
"4f": "w",
|
||||
"0e": "6",
|
||||
"5b": "c",
|
||||
"5d": "e",
|
||||
"0d": "5",
|
||||
"53": "k",
|
||||
"1e": "&",
|
||||
"5a": "b",
|
||||
"59": "a",
|
||||
"4a": "r",
|
||||
"4c": "t",
|
||||
"4e": "v",
|
||||
"57": "o",
|
||||
"51": "i",
|
||||
}
|
||||
|
||||
|
||||
def decode_hex_string(hex_string):
|
||||
# Split the hex string into pairs of characters
|
||||
hex_pairs = re.findall("..", hex_string)
|
||||
|
||||
# Decode each hex pair
|
||||
decoded_chars = [hex_to_char.get(pair.lower(), pair) for pair in hex_pairs]
|
||||
|
||||
return "".join(decoded_chars)
|
||||
|
||||
|
||||
print(
|
||||
decode_hex_string(
|
||||
"175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0a010f0d0e5e0f0a0e0b0f0d0a010e0f0e000e5e0e5a0e0b0a010d0d0e5d0e0f0f0c0e0b0e0a0a0e0c0a0e010e0d0f0b0e5a0e0b0e000f0a0f0d0a010e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a000e5a0f0e0b0a0a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0d0d0e5d0e0f0f0c0e0b0f0e0e010e5e0e000f0a0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a0c0f5a"
|
||||
)
|
||||
)
|
||||
102
lscraping/final.json
Normal file
102
lscraping/final.json
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"episode": {
|
||||
"episodeString": "12",
|
||||
"sourceUrls": [
|
||||
{
|
||||
"sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0a010a010f080e5e0e0a0f0d0f0a0f0c0e0b0e0f0e5a0e5e0e000e090a000e5e0e010a010e590e010e0f0e0a0a000f0e0e5d0f0e0b010e5e0e0a0b5a0c5a0d0a0c0b0f5d0c010d0a0e5c0b0d0a080f0a0e5e0f0a0e590e0b0b5a0d0d0f090e010f0c0e0a0a5c0c0f0f0c0f0a0a5c0c010e000e590e5e0e000e0b0a0b0b0d0c0f0a5c0c0f0e590e5e0e0d0e5e0f5b0e0f0f0a0e5e0e010e000a080f0a0f5e0f0e0e0b0f0d0f0b0e0c0b5a0d0d0d0b0c0c0a080f0d0f0b0e0c0b5a0a080e0d0e010f080e0b0f0c0b5a0d5e0b0c0b5e0b0c0d5b0d5d0c5e0f080e0d0b0d0e0a0f080e0d0e5a0d0f0f0a0d5e0d5d0c5b0b0e0c590d090b5e0f0b0e0c0c090e590f0b0d5b0d0d0b0f0e5d0e0c0c090e590e5b0e0f0d5d0f0e0e5d0e0a0c090e590f080e0c0e5e0b0b0f090e0c0e5a0e0d0b5a0a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0f080e5e0e0a0e0b0e010f0d0f0a0f0c0e0b0e0f0e5a0e5e0e010a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0f5a1e4a5d5e5d4a5d4a05",
|
||||
"priority": 7.7,
|
||||
"sourceName": "Luf-mp4",
|
||||
"type": "iframe",
|
||||
"className": "",
|
||||
"streamerId": "allanime"
|
||||
},
|
||||
{
|
||||
"sourceUrl": "//vidstreaming.io/load.php?id=MTExOTk3&title=Sword+Art+Online%3A+Alicization&typesub=SUB&sub=&cover=Y292ZXIvc3dvcmQtYXJ0LW9ubGluZS1hbGljaXphdGlvbi5wbmc=",
|
||||
"priority": 4,
|
||||
"sourceName": "Vid-mp4",
|
||||
"type": "iframe",
|
||||
"className": "",
|
||||
"streamerId": "allanime",
|
||||
"downloads": {
|
||||
"sourceName": "Gl",
|
||||
"downloadUrl": "https://embtaku.pro/download?id=MTExOTk3&title=Sword+Art+Online%3A+Alicization&typesub=SUB&sub=&cover=Y292ZXIvc3dvcmQtYXJ0LW9ubGluZS1hbGljaXphdGlvbi5wbmc=&sandbox=allow-forms%20allow-scripts%20allow-same-origin%20allow-downloads"
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceUrl": "https://streamsb.net/e/kag9w71l9jq0.html",
|
||||
"priority": 5.5,
|
||||
"sourceName": "Ss-Hls",
|
||||
"type": "iframe",
|
||||
"className": "text-danger",
|
||||
"streamerId": "allanime",
|
||||
"downloads": {
|
||||
"sourceName": "StreamSB",
|
||||
"downloadUrl": "https://streamsb.net/d/kag9w71l9jq0.html&sandbox=allow-forms%20allow-scripts%20allow-same-origin%20allow-downloads"
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0a010f080e5e0e0a0e0b0e010f0d0a010e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0a010f0d0f0b0e0c0a010b0f0b0c0a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0c0a0f0c0e010f0e0e0c0e010f5d0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0a010f080e5e0e0a0e0b0e010f0d0a010e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0a010f0d0f0b0e0c0a010b0f0b0c0a0c0f5a",
|
||||
"priority": 7,
|
||||
"sourceName": "Sak",
|
||||
"type": "iframe",
|
||||
"className": "",
|
||||
"streamerId": "allanime"
|
||||
},
|
||||
{
|
||||
"sourceUrl": "--504c4c484b0217174c5757544b165e594b4c0c4b485d5d5c164a4b4e4817174e515c5d574b1756480f40750b0a0b4e0d48700e7361015d174b4d5a17090a",
|
||||
"priority": 7.9,
|
||||
"sourceName": "Yt-mp4",
|
||||
"type": "player",
|
||||
"className": "",
|
||||
"streamerId": "allanime"
|
||||
},
|
||||
{
|
||||
"sourceUrl": "https://mp4upload.com/embed-bayej7b93in0.html",
|
||||
"priority": 4,
|
||||
"sourceName": "Mp4",
|
||||
"type": "iframe",
|
||||
"sandbox": "allow-forms allow-scripts allow-same-origin",
|
||||
"className": "",
|
||||
"streamerId": "allanime"
|
||||
},
|
||||
{
|
||||
"sourceUrl": "https://ok.ru/videoembed/2731036445330",
|
||||
"priority": 3.5,
|
||||
"sourceName": "Ok",
|
||||
"type": "iframe",
|
||||
"sandbox": "allow-forms allow-scripts allow-same-origin",
|
||||
"className": "text-info",
|
||||
"streamerId": "allanime"
|
||||
},
|
||||
{
|
||||
"sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0b0b0b080e0d0b5e0b0a0b0d0d010e0c0b5e0b0c0b080b0f0b0d0e0c0b0a0b0b0b0e0b080b090b0a0b5e0b0f0b080b5e0b5d0e0a0b080e0f0b5e0b0b0e080b0f0b0e0e0c0b0f0b5d0e0f0b0c0e0a0a0e0f590a0e0b0d0e0f0b0d0e080b0a0e0f0e080b0b0b080e0a0b090b0a0b090e0b0b5d0b5e0e0d0b0d0b0e0b0f0b0b0b080e0b0e080b080e0d0b5d0b5d0e0d0b0b0b080e0c0a0e0f590a0e0e5a0e0b0e0a0e5e0e0f0a010b0b0b080e0d0b5e0b0a0b0d0d010e0c0b5e0b0c0b080b0f0b0d0e0c0b0a0b0b0b0e0b080b090b0a0b5e0b0f0b080b5e0b5d0e0a0b080e0f0b5e0b0b0e080b0f0b0e0e0c0b0f0b5d0e0f0b0c0e0a0e080b0e0b0e0b0f0a000e5b0f0e0e090a0e0f590a0e0a590b090b0c0b0e0f0e0a590b0f0b0e0b5d0b0e0f0e0a590b0a0b5d0b0e0f0e0a590a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0d090e5e0f5d0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a0c0f5a",
|
||||
"priority": 8.5,
|
||||
"sourceName": "Default",
|
||||
"type": "iframe",
|
||||
"className": "text-info",
|
||||
"streamerId": "allanime"
|
||||
},
|
||||
{
|
||||
"sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0a010f0d0e5e0f0a0e0b0f0d0a010e0f0e000e5e0e5a0e0b0a010d0d0e5d0e0f0f0c0e0b0e0a0a0e0c0a0e010e0d0f0b0e5a0e0b0e000f0a0f0d0a010e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a000e5a0f0e0b0a0a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0d0d0e5d0e0f0f0c0e0b0f0e0e010e5e0e000f0a0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a0c0f5a",
|
||||
"priority": 7.4,
|
||||
"sourceName": "S-mp4",
|
||||
"type": "iframe",
|
||||
"className": "",
|
||||
"streamerId": "allanime",
|
||||
"downloads": {
|
||||
"sourceName": "S-mp4",
|
||||
"downloadUrl": "https://blog.allanime.day/apivtwo/clock/download?id=7d2473746a243c2429756f7263752967686f6b6329556e6774636226426965736b63687275296876317e4b3534357033764e304d5f3f6359373459757364286b7632242a2475727463676b63744f62243c24556e67746376696f6872242a2462677263243c24343634322b36302b363152373f3c333f3c3636283636365c242a24626971686a696762243c727473637b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0e5d0f0a0f0a0f0e0f0d0b5b0a010a010f0b0f0d0e0b0f0c0f0d0e0d0e590e010f0b0e0a0a000e0d0e010e5a0a010e0b0e5a0e0c0e0b0e0a0a5a0f0f0b5e0e080e590f080e5b0e5b0b0d0e0c0b090f0d0f0f0a000e5d0f0a0e5a0e590a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0d0b0f0d0e0b0f0c0f080e5e0e0a0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a0c0f5a",
|
||||
"priority": 1,
|
||||
"sourceName": "Uv-mp4",
|
||||
"type": "iframe",
|
||||
"className": "",
|
||||
"streamerId": "allanime"
|
||||
}
|
||||
],
|
||||
"notes": null
|
||||
}
|
||||
}
|
||||
1
lscraping/output.html
Normal file
1
lscraping/output.html
Normal file
@@ -0,0 +1 @@
|
||||
b'{"data":{"shows":{"edges":[{"_id":"GoSNgenDWLm2B8C3H","name":"Highspeed Etoile","availableEpisodes":{"sub":10,"dub":0,"raw":0},"__typename":"Show"},{"_id":"8Suk8xq3YmvDq2s5C","name":"Girls Band Cry","availableEpisodes":{"sub":10,"dub":0,"raw":0},"__typename":"Show"},{"_id":"rwroGT45PMJiWkeYY","name":"The iDOLM@STER Shiny Colors","availableEpisodes":{"sub":10,"dub":0,"raw":0},"__typename":"Show"},{"_id":"pNPGCMYN5yA4yQH5J","name":"Maou Gakuin no Futekigousha: Shijou Saikyou no Maou no Shiso, Tensei shite Shison-tachi no Gakkou e Kayou 2nd Season Part 2","availableEpisodes":{"sub":8,"dub":2,"raw":0},"__typename":"Show"},{"_id":"Q7h4rxYmdHDADCTfY","name":"Mahouka Koukou no Rettousei Season 3","availableEpisodes":{"sub":10,"dub":3,"raw":0},"__typename":"Show"},{"_id":"KB5XDvwPdtLFEkoQZ","name":"Tensei shitara Slime Datta Ken Season 3","availableEpisodes":{"sub":11,"dub":8,"raw":0},"__typename":"Show"},{"_id":"QvmdS433kzi6gy8EQ","name":"Astro Note","availableEpisodes":{"sub":10,"dub":0,"raw":0},"__typename":"Show"},{"_id":"A3YuyeYdar4cPvix2","name":"Nyaaaanvy","availableEpisodes":{"sub":2,"dub":0,"raw":0},"__typename":"Show"},{"_id":"vHRnxWT83vEhRzcAG","name":"Nijiyon Animation 2","availableEpisodes":{"sub":10,"dub":0,"raw":0},"__typename":"Show"},{"_id":"ZDxMfy9mmxEbjryKP","name":"Ni Tian Zhizun","availableEpisodes":{"sub":308,"dub":0,"raw":0},"__typename":"Show"},{"_id":"ox2opWHHwh2d663NG","name":"Ling Jian Zun Season 4","availableEpisodes":{"sub":394,"dub":0,"raw":0},"__typename":"Show"},{"_id":"wcGJw5u7NRiabB4qz","name":"Exclusive Possession: Young Master Ji\'s Beloved Wife Season 3","availableEpisodes":{"sub":59,"dub":0,"raw":0},"__typename":"Show"},{"_id":"M5Z6D7DXveoMSKXN7","name":"Bai Lian Cheng Shen Season 2","availableEpisodes":{"sub":28,"dub":0,"raw":0},"__typename":"Show"},{"_id":"6oDA6BDxtLCJq5FZx","name":"Wanmei Shijie","availableEpisodes":{"sub":166,"dub":0,"raw":0},"__typename":"Show"},{"_id":"5hpioXkSfCNYX4k5M","name":"Da Zhu Zai Nian Fan","availableEpisodes":{"sub":52,"dub":0,"raw":0},"__typename":"Show"},{"_id":"FDbFjEnbDTbrGJ757","name":"Yuan Zun (2024)","availableEpisodes":{"sub":4,"dub":0,"raw":0},"__typename":"Show"},{"_id":"2z6h3kbiB4xSafQcj","name":"Dead Dead Demons Dededede Destruction","availableEpisodes":{"sub":3,"dub":3,"raw":0},"__typename":"Show"},{"_id":"gYNWJBu4rvSrmSfiB","name":"Henjin no Salad Bowl","availableEpisodes":{"sub":10,"dub":0,"raw":0},"__typename":"Show"},{"_id":"eDZ9AzEzXu3LQ3uTK","name":"Muu no Hakugei","availableEpisodes":{"sub":2,"dub":0,"raw":0},"__typename":"Show"},{"_id":"dBjYbRzRyWg59E57c","name":"Chiikawa","availableEpisodes":{"sub":95,"dub":0,"raw":0},"__typename":"Show"}]}}}\n' 200
|
||||
49
lscraping/scraper.txt
Normal file
49
lscraping/scraper.txt
Normal file
@@ -0,0 +1,49 @@
|
||||
## the qraphql query
|
||||
search_gql="query(
|
||||
\$search: SearchInput
|
||||
\$limit: Int
|
||||
\$page: Int
|
||||
\$translationType: VaildTranslationTypeEnumType
|
||||
\$countryOrigin: VaildCountryOriginEnumType
|
||||
)
|
||||
{
|
||||
shows(
|
||||
search: \$search
|
||||
limit: \$limit
|
||||
page: \$page
|
||||
translationType: \$translationType
|
||||
countryOrigin: \$countryOrigin
|
||||
) {
|
||||
edges {
|
||||
_id
|
||||
name
|
||||
availableEpisodes
|
||||
__typename
|
||||
}
|
||||
}}"
|
||||
|
||||
|
||||
##allanime api endpoint
|
||||
allanime_base = allanime.day
|
||||
allanime_api = api.$allanime_base
|
||||
|
||||
##allanime referer header
|
||||
allanime_referer = "https://allanime.to/"
|
||||
|
||||
##post data
|
||||
"variables={\"search\":{\"allowAdult\":false,\"allowUnknown\":false,\"query\":\"$1\"},\"limit\":40,\"page\":1,\"translationType\":\"$mode\",\"countryOrigin\":\"ALL\"}" --data-urlencode "query=$search_gql"
|
||||
curl -e "$allanime_refr" -s -G "${allanime_api}/api" --data-urlencode "variables={\"search\":{\"allowAdult\":false,\"allowUnknown\":false,\"query\":\"$1\"},\"limit\":40,\"page\":1,\"translationType\":\"$mode\",\"countryOrigin\":\"ALL\"}" --data-urlencode "query=$search_gql" -A "$agent" | sed 's|Show|\
|
||||
|
||||
|
||||
##required in request
|
||||
user agent
|
||||
referer
|
||||
|
||||
|
||||
episodes_list_gql="query (\$showId: String!) { show( _id: \$showId ) { _id availableEpisodesDetail }}"
|
||||
curl -e "$allanime_refr" -s -G "${allanime_api}/api" --data-urlencode "variables={\"showId\":\"$*\"}" --data-urlencode "query=$episodes_list_gql" -A "$agent" | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\
|
||||
|
||||
|
||||
episode_embed_gql="query (\$showId: String!, \$translationType: VaildTranslationTypeEnumType!, \$episodeString: String!) { episode( showId: \$showId translationType: \$translationType episodeString: \$episodeString ) { episodeString sourceUrls }}"
|
||||
resp=$(curl -e "$allanime_refr" -s -G "${allanime_api}/api" --data-urlencode "variables={\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"}" --data-urlencode "query=$episode_embed_gql" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":"--([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p')
|
||||
|
||||
72
lscraping/scrapy.py
Normal file
72
lscraping/scrapy.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import requests
|
||||
|
||||
# graphql queries
|
||||
|
||||
search_gql = """
|
||||
query(
|
||||
$search: SearchInput,
|
||||
$limit: Int,
|
||||
$page: Int,
|
||||
$translationType: VaildTranslationTypeEnumType,
|
||||
$countryOrigin: VaildCountryOriginEnumType
|
||||
) {
|
||||
shows(
|
||||
search: $search,
|
||||
limit: $limit,
|
||||
page: $page,
|
||||
translationType: $translationType,
|
||||
countryOrigin: $countryOrigin
|
||||
) {
|
||||
edges {
|
||||
_id
|
||||
name
|
||||
availableEpisodes
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
# allanime constants
|
||||
agent = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0"
|
||||
)
|
||||
allanime_base = "allanime.day"
|
||||
allanime_api = f"https://api.{allanime_base}/api"
|
||||
|
||||
##allanime referer header
|
||||
allanime_referer = "https://allanime.to"
|
||||
|
||||
|
||||
search = {"allowAdult": False, "allowUnknown": False, "query": "one piece"}
|
||||
limit = 40
|
||||
translationType = "sub" # dub
|
||||
countryOrigin = "ALL"
|
||||
page = 1
|
||||
|
||||
variables = {
|
||||
"search": search,
|
||||
"limit": limit,
|
||||
"page": page,
|
||||
"translationType": translationType,
|
||||
"countryOrigin": countryOrigin,
|
||||
}
|
||||
|
||||
query = search_gql
|
||||
resp = requests.get(
|
||||
allanime_api,
|
||||
params={"variables": variables, "query": query},
|
||||
headers={"User-Agent": agent, "Referer": allanime_referer},
|
||||
)
|
||||
|
||||
print(resp.json(), resp.status_code)
|
||||
|
||||
|
||||
episodes_list_gql = """
|
||||
query ($showId: String!) {
|
||||
show(_id: $showId) {
|
||||
_id availableEpisodesDetail
|
||||
}
|
||||
}
|
||||
"""
|
||||
# curl -e "$allanime_refr" -s -G "${allanime_api}/api" --data-urlencode "variables={\"showId\":\"$*\"}" --data-urlencode "query=$episodes_list_gql" -A "$agent" | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"venv": ".venv",
|
||||
"venvPath": "."
|
||||
"venvPath": ".",
|
||||
"typeCheckingMode": "standard"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user