diff --git a/fastanime/__main__.py b/fastanime/__main__.py
index e1d06a5..59b8227 100644
--- a/fastanime/__main__.py
+++ b/fastanime/__main__.py
@@ -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:
diff --git a/lscraping/allanime_api.py b/lscraping/allanime_api.py
new file mode 100644
index 0000000..6575dcb
--- /dev/null
+++ b/lscraping/allanime_api.py
@@ -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])
diff --git a/lscraping/anicli.sh b/lscraping/anicli.sh
new file mode 100644
index 0000000..03e46b2
--- /dev/null
+++ b/lscraping/anicli.sh
@@ -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
+ 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 .
+#
+# Project repository: https://github.com/pystardust/ani-cli
diff --git a/lscraping/data.json b/lscraping/data.json
new file mode 100644
index 0000000..c44b96b
--- /dev/null
+++ b/lscraping/data.json
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/lscraping/data2.json b/lscraping/data2.json
new file mode 100644
index 0000000..649c61f
--- /dev/null
+++ b/lscraping/data2.json
@@ -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"}]}}
\ No newline at end of file
diff --git a/lscraping/data3.json b/lscraping/data3.json
new file mode 100644
index 0000000..b091db0
--- /dev/null
+++ b/lscraping/data3.json
@@ -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"
+ }
+ ]
+ }
+}
diff --git a/lscraping/f.py b/lscraping/f.py
new file mode 100644
index 0000000..d5cf5dc
--- /dev/null
+++ b/lscraping/f.py
@@ -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"
+ )
+)
diff --git a/lscraping/final.json b/lscraping/final.json
new file mode 100644
index 0000000..8a8a089
--- /dev/null
+++ b/lscraping/final.json
@@ -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
+ }
+}
diff --git a/lscraping/output.html b/lscraping/output.html
new file mode 100644
index 0000000..c1c8a59
--- /dev/null
+++ b/lscraping/output.html
@@ -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
diff --git a/lscraping/scraper.txt b/lscraping/scraper.txt
new file mode 100644
index 0000000..814fd66
--- /dev/null
+++ b/lscraping/scraper.txt
@@ -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')
+
diff --git a/lscraping/scrapy.py b/lscraping/scrapy.py
new file mode 100644
index 0000000..c4ec7af
--- /dev/null
+++ b/lscraping/scrapy.py
@@ -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|,|\
diff --git a/pyrightconfig.json b/pyrightconfig.json
index 2d49b3a..4703a54 100644
--- a/pyrightconfig.json
+++ b/pyrightconfig.json
@@ -1,4 +1,5 @@
{
"venv": ".venv",
- "venvPath": "."
+ "venvPath": ".",
+ "typeCheckingMode": "standard"
}