From 1d2f7d00438380949afa6c3bed50b73bd5282e64 Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Thu, 16 Apr 2026 14:18:02 +0200 Subject: [PATCH 1/8] fix: Allanime openssl aes-256-ctr decryption --- ani-cli | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ani-cli b/ani-cli index 43984cc6f..e5ae3e499 100755 --- a/ani-cli +++ b/ani-cli @@ -208,13 +208,35 @@ select_quality() { episode=$(printf "%s" "$result" | cut -d'>' -f2) } +decode_tobeparsed() { + blob="$1" + tmp="$(mktemp)" + ct="$(mktemp)" + key="$(printf '%s' 'SimtVuagFbGR2K7P' | openssl dgst -sha256 -binary | od -A n -t x1 | tr -d ' \n')" + printf '%s' "$blob" | openssl enc -d -base64 -A >"$tmp" + len="$(wc -c <"$tmp" | tr -d ' ')" + iv="$(dd if="$tmp" bs=1 count=12 2>/dev/null | od -A n -t x1 | tr -d ' \n')" + ct_len=$((len - 28)) + dd if="$tmp" bs=1 skip=12 count="$ct_len" 2>/dev/null >"$ct" + ctr="${iv}00000002" + plain="$(openssl enc -d -aes-256-ctr -K "$key" -iv "$ctr" -nosalt -nopad <"$ct" 2>/dev/null)" + rm -f "$tmp" "$ct" + printf '%s' "$plain" | tr '{}' '\n' | sed -nE 's|.*"sourceUrl":"--([^"]*)".*"sourceName":"([^"]*)".*|\2 :\1|p' +} + # 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 #shellcheck disable=SC2016 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 -H "Content-Type: application/json" -X POST "${allanime_api}/api" --data "{\"variables\":{\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"},\"query\":\"$episode_embed_gql\"}" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":"--([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p') + api_resp="$(curl -e "$allanime_refr" -s -H "Content-Type: application/json" -X POST "${allanime_api}/api" --data "{\"variables\":{\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"},\"query\":\"$episode_embed_gql\"}" -A "$agent")" + if printf "%s" "$api_resp" | grep -q '"tobeparsed"'; then + blob="$(printf "%s" "$api_resp" | sed -nE 's|.*"tobeparsed":"([^"]*)".*|\1|p')" + resp="$(decode_tobeparsed "$blob")" + else + resp="$(printf "%s" "$api_resp" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":"--([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p')" + fi # generate links into sequential files cache_dir="$(mktemp -d)" providers="1 2 3 4" From 72f585cd7894dc24c7f0c6ce032cb9653d67fa47 Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Thu, 16 Apr 2026 14:21:52 +0200 Subject: [PATCH 2/8] chore: bump version number --- ani-cli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ani-cli b/ani-cli index e5ae3e499..c3b512ac5 100755 --- a/ani-cli +++ b/ani-cli @@ -1,6 +1,6 @@ #!/bin/sh -version_number="4.11.0" +version_number="4.12.0" # UI From 15d3414060bfb0d2d9ed1302a94f683aab961c9c Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Fri, 17 Apr 2026 09:35:50 +0200 Subject: [PATCH 3/8] chore: add openssl to README and dep_ch --- README.md | 3 ++- ani-cli | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7fe0562c4..1994a2290 100644 --- a/README.md +++ b/README.md @@ -507,7 +507,8 @@ apk del grep sed curl fzf git aria2 ffmpeg ncurses - yt-dlp - m3u8 Downloader - ffmpeg - m3u8 Downloader (fallback) - fzf - User interface -- ani-skip (optional) +- openssl (for decrypting encrypted video sources) +- ani-skip (optional, for auto-skipping anime intros) - patch - Self updating ### Ani-Skip diff --git a/ani-cli b/ani-cli index c3b512ac5..cabf4a4b6 100755 --- a/ani-cli +++ b/ani-cli @@ -511,7 +511,7 @@ done [ "$use_external_menu" = "1" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-"-multi-select"}" [ "$external_menu_normal_window" = "1" ] && external_menu_args="-normal-window" printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n" -dep_ch "curl" "sed" "grep" || true +dep_ch "curl" "sed" "grep" "openssl" || true [ "$skip_intro" = 1 ] && (dep_ch "ani-skip" || true) dep_ch "fzf" || true case "$player_function" in From 2f4494af080ed0c12ea42de40ae42d4c8cc385e5 Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Sat, 18 Apr 2026 20:25:50 +0200 Subject: [PATCH 4/8] fix: compute allanime_key once at startup --- ani-cli | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ani-cli b/ani-cli index cabf4a4b6..39ca0240c 100755 --- a/ani-cli +++ b/ani-cli @@ -212,14 +212,13 @@ decode_tobeparsed() { blob="$1" tmp="$(mktemp)" ct="$(mktemp)" - key="$(printf '%s' 'SimtVuagFbGR2K7P' | openssl dgst -sha256 -binary | od -A n -t x1 | tr -d ' \n')" printf '%s' "$blob" | openssl enc -d -base64 -A >"$tmp" len="$(wc -c <"$tmp" | tr -d ' ')" iv="$(dd if="$tmp" bs=1 count=12 2>/dev/null | od -A n -t x1 | tr -d ' \n')" ct_len=$((len - 28)) dd if="$tmp" bs=1 skip=12 count="$ct_len" 2>/dev/null >"$ct" ctr="${iv}00000002" - plain="$(openssl enc -d -aes-256-ctr -K "$key" -iv "$ctr" -nosalt -nopad <"$ct" 2>/dev/null)" + plain="$(openssl enc -d -aes-256-ctr -K "$allanime_key" -iv "$ctr" -nosalt -nopad <"$ct" 2>/dev/null)" rm -f "$tmp" "$ct" printf '%s' "$plain" | tr '{}' '\n' | sed -nE 's|.*"sourceUrl":"--([^"]*)".*"sourceName":"([^"]*)".*|\2 :\1|p' } @@ -409,6 +408,7 @@ agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefo allanime_refr="https://allmanga.to" allanime_base="allanime.day" allanime_api="https://api.${allanime_base}" +allanime_key="$(printf '%s' 'SimtVuagFbGR2K7P' | openssl dgst -sha256 -binary | od -A n -t x1 | tr -d ' \n')" mode="${ANI_CLI_MODE:-sub}" download_dir="${ANI_CLI_DOWNLOAD_DIR:-.}" log_episode="${ANI_CLI_LOG:-1}" From 251f0a93defcd028513ab04b8a3acba2d6ea686d Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Sat, 18 Apr 2026 20:26:04 +0200 Subject: [PATCH 5/8] fix: use base64 coreutils instead of openssl for b64 decoding --- ani-cli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ani-cli b/ani-cli index 39ca0240c..1139811d7 100755 --- a/ani-cli +++ b/ani-cli @@ -212,7 +212,7 @@ decode_tobeparsed() { blob="$1" tmp="$(mktemp)" ct="$(mktemp)" - printf '%s' "$blob" | openssl enc -d -base64 -A >"$tmp" + printf '%s' "$blob" | base64 -d >"$tmp" len="$(wc -c <"$tmp" | tr -d ' ')" iv="$(dd if="$tmp" bs=1 count=12 2>/dev/null | od -A n -t x1 | tr -d ' \n')" ct_len=$((len - 28)) From bd6d5c8ffd6691b7c4a5319bd0f6cfbe249eeae8 Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Sat, 18 Apr 2026 20:32:00 +0200 Subject: [PATCH 6/8] fix: remove temp files and redundant vars from decode_tobeparsed --- ani-cli | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/ani-cli b/ani-cli index 1139811d7..cb8b62ac8 100755 --- a/ani-cli +++ b/ani-cli @@ -209,17 +209,12 @@ select_quality() { } decode_tobeparsed() { - blob="$1" tmp="$(mktemp)" - ct="$(mktemp)" - printf '%s' "$blob" | base64 -d >"$tmp" - len="$(wc -c <"$tmp" | tr -d ' ')" + printf '%s' "$1" | base64 -d >"$tmp" iv="$(dd if="$tmp" bs=1 count=12 2>/dev/null | od -A n -t x1 | tr -d ' \n')" - ct_len=$((len - 28)) - dd if="$tmp" bs=1 skip=12 count="$ct_len" 2>/dev/null >"$ct" ctr="${iv}00000002" - plain="$(openssl enc -d -aes-256-ctr -K "$allanime_key" -iv "$ctr" -nosalt -nopad <"$ct" 2>/dev/null)" - rm -f "$tmp" "$ct" + plain="$(dd if="$tmp" bs=1 skip=12 2>/dev/null | openssl enc -d -aes-256-ctr -K "$allanime_key" -iv "$ctr" -nosalt -nopad 2>/dev/null)" + rm -f "$tmp" printf '%s' "$plain" | tr '{}' '\n' | sed -nE 's|.*"sourceUrl":"--([^"]*)".*"sourceName":"([^"]*)".*|\2 :\1|p' } From 3b9424014d1b776ae52217e939db36a689ce2d1f Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Sun, 19 Apr 2026 17:03:52 +0200 Subject: [PATCH 7/8] docs: openssl on termux --- README.md | 7 ++++++- ani-cli | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1994a2290..fc73608da 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,11 @@ pkg install termux-am For players you can use the apk (playstore/fdroid) versions of mpv and vlc. Note that these cannot be checked from termux so a warning is generated when checking dependencies. +**Note:** The `openssl` CLI utility is in the `openssl-tool` package on Termux, not `openssl`. Install it with: +```sh +pkg install openssl-tool +``` + ### Tier 2 Support: Windows, WSL, iOS, Steam Deck, FreeBSD @@ -507,7 +512,7 @@ apk del grep sed curl fzf git aria2 ffmpeg ncurses - yt-dlp - m3u8 Downloader - ffmpeg - m3u8 Downloader (fallback) - fzf - User interface -- openssl (for decrypting encrypted video sources) +- openssl (for decrypting encrypted video sources; on Termux, the CLI is in the `openssl-tool` package) - ani-skip (optional, for auto-skipping anime intros) - patch - Self updating diff --git a/ani-cli b/ani-cli index cb8b62ac8..36da3afda 100755 --- a/ani-cli +++ b/ani-cli @@ -506,7 +506,11 @@ done [ "$use_external_menu" = "1" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-"-multi-select"}" [ "$external_menu_normal_window" = "1" ] && external_menu_args="-normal-window" printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n" -dep_ch "curl" "sed" "grep" "openssl" || true +dep_ch "curl" "sed" "grep" || true +case "$(uname -o 2>/dev/null)" in + *ndroid*) command -v openssl >/dev/null || die 'Program "openssl" not found. On Termux, install with: pkg install openssl-tool' ;; + *) dep_ch "openssl" || true ;; +esac [ "$skip_intro" = 1 ] && (dep_ch "ani-skip" || true) dep_ch "fzf" || true case "$player_function" in From d778ca9c2cc0d9b72f9666b004da93ca3306d07e Mon Sep 17 00:00:00 2001 From: pixel-peeper Date: Tue, 19 May 2026 19:53:13 +0100 Subject: [PATCH 8/8] Fix allanime API scraper and update_script --- ani-cli | 214 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 152 insertions(+), 62 deletions(-) diff --git a/ani-cli b/ani-cli index 36da3afda..70e5ba871 100755 --- a/ani-cli +++ b/ani-cli @@ -1,6 +1,6 @@ #!/bin/sh -version_number="4.12.0" +version_number="4.14.1" # UI @@ -105,15 +105,17 @@ version_info() { } update_script() { - update="$(curl -s -A "$agent" "https://raw.githubusercontent.com/pystardust/ani-cli/master/ani-cli")" || die "Connection error" + printf "[branch:%s] Checking for Update..\n" "$branch" + update="$(curl --fail-with-body -sL -A "$agent" -H "Accept: application/vnd.github.v3.raw" "https://api.github.com/repos/pystardust/ani-cli/contents/ani-cli?ref=$branch")" || die "[branch:$branch] Connection error: $update" + printf '%s' "$update" | grep -q "^version_number" || die "[branch:$branch] Invalid Response." update="$(printf '%s\n' "$update" | diff -u "$0" -)" if [ -z "$update" ]; then - printf "Script is up to date :)\n" + printf "[branch:%s] Script is up to date :)\n" "$branch" else if printf '%s\n' "$update" | patch "$0" -; then - printf "Script has been updated\n" + printf "[branch:%s] Script has been updated\n" "$branch" else - die "Can't update for some reason!" + die "[branch:$branch] Can't update for some reason!" fi fi exit 0 @@ -136,13 +138,29 @@ where_mpv() { printf "%s" "mpv" && return 0 } +cleanup_shit() { + [ "$player_function" = "android_mpv" ] && : >"/storage/emulated/0/mpv/mpv.config.mp4" + printf "\033[0m" # clears colors + rm -f "${histfile}.new" # remove temp logfile +} + # SCRAPING # extract the video links from response of embed urls, extract mp4 links form m3u8 lists get_links() { - response="$(curl -e "$allanime_refr" -s "https://${allanime_base}$*" -A "$agent")" - episode_link="$(printf '%s' "$response" | sed 's|},{|\ -|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p;s|.*hls","url":"([^"]*)".*"hardsub_lang":"en-US".*|\1|p')" + case "$*" in + *mp4upload*) + episode_link=$(curl --max-time 10 -sLk "$*" -A "$agent" -e "$allanime_refr" | sed -nE 's|.*src: "([^"]*)"[[:space:]]*|Mp4Upload >\1|p') + ;; + *tools.fast4speed.rsvp*) + episode_link=$(printf "%s\n" "Yt >$*") + ;; + *) + response="$(curl -e "$allanime_refr" -s "https://${allanime_base}$*" -A "$agent")" + episode_link="$(printf '%s' "$response" | sed 's|},{|\ + |g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p;s|.*hls","url":"([^"]*)".*"hardsub_lang":"en-US".*|\1|p')" + ;; + esac case "$episode_link" in *repackager.wixmp.com*) @@ -157,22 +175,19 @@ get_links() { extract_link=$(printf "%s" "$episode_link" | head -1 | cut -d'>' -f2) relative_link=$(printf "%s" "$extract_link" | sed 's|[^/]*$||') m3u8_streams="$(curl -e "$m3u8_refr" -s "$extract_link" -A "$agent")" - printf "%s" "$m3u8_streams" | grep -q "EXTM3U" && printf "%s" "$m3u8_streams" | sed 's|^#EXT-X-STREAM.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|;/EXT-X-I-FRAME/d' | - sed "s|>|cc>${relative_link}|g" | sort -nr - printf '%s' "$response" | sed -nE 's|.*"subtitles":\[\{"lang":"en","label":"English","default":"default","src":"([^"]*)".*|subtitle >\1|p' >"$cache_dir/suburl" + printf "%s" "$m3u8_streams" | grep -q "EXTM3U" && printf "%s" "$m3u8_streams" | sed 's|^#EXT-X-STREAM.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|;/EXT-X-I-FRAME/d' | sed "s|>|>${relative_link}|g" | sort -nr ;; *) [ -n "$episode_link" ] && printf "%s\n" "$episode_link" ;; esac - - printf "%s" "$*" | grep -q "tools.fast4speed.rsvp" && printf "%s\n" "Yt >$*" printf "\033[1;32m%s\033[0m Links Fetched\n" "$provider_name" 1>&2 } # initialises 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/^79$/A/g;s/^7a$/B/g;s/^7b$/C/g;s/^7c$/D/g;s/^7d$/E/g;s/^7e$/F/g;s/^7f$/G/g;s/^70$/H/g;s/^71$/I/g;s/^72$/J/g;s/^73$/K/g;s/^74$/L/g;s/^75$/M/g;s/^76$/N/g;s/^77$/O/g;s/^68$/P/g;s/^69$/Q/g;s/^6a$/R/g;s/^6b$/S/g;s/^6c$/T/g;s/^6d$/U/g;s/^6e$/V/g;s/^6f$/W/g;s/^60$/X/g;s/^61$/Y/g;s/^62$/Z/g;s/^59$/a/g;s/^5a$/b/g;s/^5b$/c/g;s/^5c$/d/g;s/^5d$/e/g;s/^5e$/f/g;s/^5f$/g/g;s/^50$/h/g;s/^51$/i/g;s/^52$/j/g;s/^53$/k/g;s/^54$/l/g;s/^55$/m/g;s/^56$/n/g;s/^57$/o/g;s/^48$/p/g;s/^49$/q/g;s/^4a$/r/g;s/^4b$/s/g;s/^4c$/t/g;s/^4d$/u/g;s/^4e$/v/g;s/^4f$/w/g;s/^40$/x/g;s/^41$/y/g;s/^42$/z/g;s/^08$/0/g;s/^09$/1/g;s/^0a$/2/g;s/^0b$/3/g;s/^0c$/4/g;s/^0d$/5/g;s/^0e$/6/g;s/^0f$/7/g;s/^00$/8/g;s/^01$/9/g;s/^15$/-/g;s/^16$/./g;s/^67$/_/g;s/^46$/~/g;s/^02$/:/g;s/^17$/\//g;s/^07$/?/g;s/^1b$/#/g;s/^63$/\[/g;s/^65$/\]/g;s/^78$/@/g;s/^19$/!/g;s/^1c$/$/g;s/^1e$/&/g;s/^10$/\(/g;s/^11$/\)/g;s/^12$/*/g;s/^13$/+/g;s/^14$/,/g;s/^03$/;/g;s/^05$/=/g;s/^1d$/%/g' | tr -d '\n' | sed "s/\/clock/\/clock\.json/") + provider_id=$(printf "%s" "$resp" | sed -n "$2" | head -1 | cut -d':' -f2-) + printf '%s' "$provider_id" | grep -qE "^--" && provider_id=$(printf "%s" "$provider_id" | sed 's/../&\ +/g' | sed 's/^--$/\n/g;s/^79$/A/g;s/^7a$/B/g;s/^7b$/C/g;s/^7c$/D/g;s/^7d$/E/g;s/^7e$/F/g;s/^7f$/G/g;s/^70$/H/g;s/^71$/I/g;s/^72$/J/g;s/^73$/K/g;s/^74$/L/g;s/^75$/M/g;s/^76$/N/g;s/^77$/O/g;s/^68$/P/g;s/^69$/Q/g;s/^6a$/R/g;s/^6b$/S/g;s/^6c$/T/g;s/^6d$/U/g;s/^6e$/V/g;s/^6f$/W/g;s/^60$/X/g;s/^61$/Y/g;s/^62$/Z/g;s/^59$/a/g;s/^5a$/b/g;s/^5b$/c/g;s/^5c$/d/g;s/^5d$/e/g;s/^5e$/f/g;s/^5f$/g/g;s/^50$/h/g;s/^51$/i/g;s/^52$/j/g;s/^53$/k/g;s/^54$/l/g;s/^55$/m/g;s/^56$/n/g;s/^57$/o/g;s/^48$/p/g;s/^49$/q/g;s/^4a$/r/g;s/^4b$/s/g;s/^4c$/t/g;s/^4d$/u/g;s/^4e$/v/g;s/^4f$/w/g;s/^40$/x/g;s/^41$/y/g;s/^42$/z/g;s/^08$/0/g;s/^09$/1/g;s/^0a$/2/g;s/^0b$/3/g;s/^0c$/4/g;s/^0d$/5/g;s/^0e$/6/g;s/^0f$/7/g;s/^00$/8/g;s/^01$/9/g;s/^15$/-/g;s/^16$/./g;s/^67$/_/g;s/^46$/~/g;s/^02$/:/g;s/^17$/\//g;s/^07$/?/g;s/^1b$/#/g;s/^63$/\[/g;s/^65$/\]/g;s/^78$/@/g;s/^19$/!/g;s/^1c$/$/g;s/^1e$/&/g;s/^10$/\(/g;s/^11$/\)/g;s/^12$/*/g;s/^13$/+/g;s/^14$/,/g;s/^03$/;/g;s/^05$/=/g;s/^1d$/%/g' | tr -d '\n' | sed "s/\/clock/\/clock\.json/") } # generates links based on given provider @@ -181,15 +196,20 @@ generate_link() { 1) provider_init "wixmp" "/Default :/p" ;; # wixmp(default)(m3u8)(multi) -> (mp4)(multi) 2) provider_init "youtube" "/Yt-mp4 :/p" ;; # youtube(mp4)(single) 3) provider_init "sharepoint" "/S-mp4 :/p" ;; # sharepoint(mp4)(single) - *) provider_init "hianime" "/Luf-Mp4 :/p" ;; # hianime(m3u8)(multi) + 4) provider_init "mp4upload" "/Mp4 :/p" ;; # mp4upload(mp4)(single) + *) + # provider_init "Filemoon" "/Fm-Hls :/p" + # get_filemoon_links "$provider_id" + # return 0 + ;; # filemoon(m3u8)(single) esac [ -n "$provider_id" ] && get_links "$provider_id" } select_quality() { - # removing urls which have soft subs to avoid playing on android, iSH and vlc (m3u8 streams don't get correct referrer) - printf '%s' "$player_function" | cut -f1 -d" " | grep -qE '(android|iSH|vlc)' && links=$(printf '%s' "$links" | sed '/cc>/d;/subtitle >/d;/m3u8_refr >/d') - printf '%s' "$player_function" | cut -f1 -d" " | grep -qE '(android|iSH)' && links=$(printf '%s' "$links" | sed '/Yt >/d') + # removing urls which have referrer to avoid playing on iSH and vlc (m3u8 streams don't get correct referrer) + printf '%s' "$player_function" | cut -f1 -d" " | grep -qE '(android_vlc|iSH|vlc)' && links=$(printf '%s' "$links" | sed '/m3u8_refr >/d') + printf '%s' "$player_function" | cut -f1 -d" " | grep -qE '(android_vlc|iSH)' && links=$(printf '%s' "$links" | sed '/Yt >/d') case "$1" in best) result=$(printf "%s" "$links" | head -n1) ;; worst) result=$(printf "%s" "$links" | grep -E '^[0-9]{3,4}' | tail -n1) ;; @@ -197,42 +217,97 @@ select_quality() { esac [ -z "$result" ] && printf "Specified quality not found, defaulting to best\n" 1>&2 && result=$(printf "%s" "$links" | head -n1) - # add refr,sub flags for m3u8 and refr flag for yt - printf '%s' "$result" | grep -q "cc>" && subtitle="$(printf '%s' "$links" | sed -nE 's|subtitle >(.*)|\1|p')" && - [ -n "$subtitle" ] && subs_flag="--sub-file=$subtitle" - printf '%s' "$result" | grep -q "cc>" && m3u8_refr="$(printf '%s' "$links" | sed -nE 's|m3u8_refr >(.*)|\1|p')" && refr_flag="--referrer=$m3u8_refr" - printf "%s" "$result" | grep -q "tools.fast4speed.rsvp" && refr_flag="--referrer=$allanime_refr" + # add refr for m3u8 and refr flag for yt + case "$result" in + *mp4upload*) + refr_flag="--referrer=https://www.mp4upload.com" + ;; + *sharepoint*) + unset refr_flag + ;; + *) + refr_flag="--referrer=$allanime_refr" + ;; + esac - ! (printf '%s' "$result" | grep -qE "(cc>|tools.fast4speed.rsvp)") && unset refr_flag - ! (printf '%s' "$result" | grep -q "cc>") && unset subs_flag episode=$(printf "%s" "$result" | cut -d'>' -f2) } -decode_tobeparsed() { +process_response() { + if ! printf '%s' "$1" | grep -q '"tobeparsed"'; then + printf '%s' "$1" + return 0 + fi + tmp="$(mktemp)" - printf '%s' "$1" | base64 -d >"$tmp" - iv="$(dd if="$tmp" bs=1 count=12 2>/dev/null | od -A n -t x1 | tr -d ' \n')" + printf '%s' "$1" | sed -nE 's|.*"tobeparsed":"([^"]*)".*|\1|p' | base64 -d >"$tmp" + file_size="$(wc -c <"$tmp")" + iv="$(dd if="$tmp" bs=1 skip=1 count=12 2>/dev/null | od -A n -t x1 | tr -d ' \n')" ctr="${iv}00000002" - plain="$(dd if="$tmp" bs=1 skip=12 2>/dev/null | openssl enc -d -aes-256-ctr -K "$allanime_key" -iv "$ctr" -nosalt -nopad 2>/dev/null)" + ct_len=$((file_size - 13 - 16)) + dd if="$tmp" bs=1 skip=13 count="$ct_len" 2>/dev/null | openssl enc -d -aes-256-ctr -K "$allanime_key" -iv "$ctr" -nosalt -nopad 2>/dev/null + rm -f "$tmp" +} + +b64url_to_hex() { + _len=$(printf '%s' "$1" | wc -c | tr -d ' ') + _mod=$((_len % 4)) + case $_mod in + 2) _pad="==" ;; + 3) _pad="=" ;; + *) _pad="" ;; + esac + printf '%s%s' "$1" "$_pad" | tr -- '-_' '+/' | base64 -d | od -A n -t x1 | tr -d ' \n' +} + +get_filemoon_links() { + response="$(curl -e "$allanime_refr" -s "https://${allanime_base}$1" -A "$agent")" + _fm_json="$(printf '%s' "$response" | tr -d '\n ' | tr ',' '\n')" + iv="$(printf '%s' "$_fm_json" | sed -nE 's|^"iv":"([^"]*)"$|\1|p')" + payload="$(printf '%s' "$_fm_json" | sed -nE 's|^"payload":"([^"]*)"$|\1|p')" + kp1="$(printf '%s' "$_fm_json" | sed -nE 's|^"key_parts":\["([^"]*)"$|\1|p')" + kp2="$(printf '%s' "$_fm_json" | sed -nE 's|^"([A-Za-z0-9_-]+)"\]$|\1|p' | head -1)" + _kp1_hex="$(b64url_to_hex "$kp1")" + _kp2_hex="$(b64url_to_hex "$kp2")" + key_hex="$_kp1_hex$_kp2_hex" + iv_hex="$(b64url_to_hex "$iv")00000002" + tmp="$(mktemp)" + _fm_len=$(printf '%s' "$payload" | wc -c | tr -d ' ') + _fm_mod=$((_fm_len % 4)) + case $_fm_mod in + 2) _fm_pad="==" ;; + 3) _fm_pad="=" ;; + *) _fm_pad="" ;; + esac + printf '%s%s' "$payload" "$_fm_pad" | tr -- '-_' '+/' | base64 -d >"$tmp" + ct_len=$(($(wc -c <"$tmp") - 16)) + plain="$(dd if="$tmp" bs=1 count="$ct_len" 2>/dev/null | openssl enc -d -aes-256-ctr -K "$key_hex" -iv "$iv_hex" -nosalt -nopad 2>/dev/null)" rm -f "$tmp" - printf '%s' "$plain" | tr '{}' '\n' | sed -nE 's|.*"sourceUrl":"--([^"]*)".*"sourceName":"([^"]*)".*|\2 :\1|p' + printf '%s' "$plain" | tr '{}[]' '\n' | + sed -nE 's|.*"url":"([^"]*)".*"height":([0-9]+).*|\2 >\1|p;s|.*"height":([0-9]+).*"url":"([^"]*)".*|\1 >\2|p' | + sed 's|\\u0026|\&|g;s|\\u003D|=|g' | sort -rn + printf "\033[1;32m%s\033[0m Links Fetched\n" "Filemoon" 1>&2 } # 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 #shellcheck disable=SC2016 episode_embed_gql='query ($showId: String!, $translationType: VaildTranslationTypeEnumType!, $episodeString: String!) { episode( showId: $showId translationType: $translationType episodeString: $episodeString ) { episodeString sourceUrls }}' - api_resp="$(curl -e "$allanime_refr" -s -H "Content-Type: application/json" -X POST "${allanime_api}/api" --data "{\"variables\":{\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"},\"query\":\"$episode_embed_gql\"}" -A "$agent")" - if printf "%s" "$api_resp" | grep -q '"tobeparsed"'; then - blob="$(printf "%s" "$api_resp" | sed -nE 's|.*"tobeparsed":"([^"]*)".*|\1|p')" - resp="$(decode_tobeparsed "$blob")" - else - resp="$(printf "%s" "$api_resp" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":"--([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p')" + query_hash="d405d0edd690624b66baba3068e0edc3ac90f1597d898a1ec8db4e5c43c00fec" + query_vars="{\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"}" + query_ext="{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"$query_hash\"}}" + + api_resp="$(curl -e "$allanime_refr" -sG -A "$agent" -H "Origin: ${allanime_refr}" "${allanime_api}/api" --data-urlencode "variables=${query_vars}" --data-urlencode "extensions=${query_ext}")" + + if [ -z "$api_resp" ] || ! printf "%s" "$api_resp" | grep -q "tobeparsed"; then + api_resp="$(curl -e "$allanime_refr" -s -H "Content-Type: application/json" -X POST "${allanime_api}/api" --data "{\"variables\":{\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"},\"query\":\"$episode_embed_gql\"}" -A "$agent")" fi + + resp="$(process_response "$api_resp" | 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 5" providers="1 2 3 4" for provider in $providers; do generate_link "$provider" >"$cache_dir"/"$provider" & @@ -302,8 +377,6 @@ update_history() { } download() { - # download subtitle if it's set - [ -n "$subtitle" ] && curl -s "$subtitle" -o "$download_dir/$2.vtt" case $1 in *m3u8*) if command -v "yt-dlp" >/dev/null; then @@ -311,16 +384,25 @@ download() { else ffmpeg -extension_picky 0 -referer "$m3u8_refr" -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" fi - # embed subs into downloads - # [ -e "$download_dir/$2.vtt" ] && ffmpeg -i "$download_dir/$2.mp4" -i "$download_dir/$2.vtt" -c copy -c:s mov_text "$download_dir/$2.bak.mp4" && mv "$download_dir/$2.bak.mp4" "$download_dir/$2.mp4" ;; *) # shellcheck disable=SC2086 - aria2c --referer="$allanime_refr" --enable-rpc=false --check-certificate=false --continue $iSH_DownFix --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide + aria2c --referer="${refr_flag#--referrer=}" --enable-rpc=false --check-certificate=false --continue $iSH_DownFix --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; esac } +android_mpv() { + dir="/storage/emulated/0/mpv" + file="$dir/mpv.config.mp4" + if [ -w "${dir%*/mpv}" ]; then + [ -d "${dir}" ] || mkdir "${dir}" + printf "%s" "$3" | sed 's| --|\n|g; s|^--||g' >"$file" + fi + am start --user 0 -a android.intent.action.VIEW -d "$1" -n is.xyz.mpv/.MPVActivity -e "title" "$2" >/dev/null 2>&1 + unset dir file +} + play_episode() { [ "$log_episode" = 1 ] && [ "$player_function" != "debug" ] && [ "$player_function" != "download" ] && command -v logger >/dev/null && logger -t ani-cli "${allanime_title}${ep_no}" [ "$skip_intro" = 1 ] && skip_flag="$(ani-skip -q "$mal_id" -e "$ep_no")" @@ -333,30 +415,29 @@ play_episode() { ;; mpv*) if [ "$no_detach" = 0 ]; then - nohup $player_function $skip_flag --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" $subs_flag $refr_flag >/dev/null 2>&1 & + nohup $player_function $skip_flag --tls-verify=no --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" $refr_flag >/dev/null 2>&1 & else - $player_function $skip_flag $subs_flag $refr_flag --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" + $player_function $skip_flag $refr_flag --tls-verify=no --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" mpv_exitcode=$? [ "$exit_after_play" = 1 ] && [ -z "$range" ] && 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_mpv) "$player_function" "$episode" "${allanime_title}Episode ${ep_no}" "--tls-verify=no $refr_flag" 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*) - [ -n "$subs_flag" ] && subs_flag="--mpv-${subs_flag#--}" [ -n "$refr_flag" ] && refr_flag="--mpv-${refr_flag#--}" if pgrep -f "IINA" >/dev/null 2>&1; then # omit --keep-running when an IINA instance exists to prevent hanging - nohup $player_function --no-stdin --mpv-force-media-title="${allanime_title}Episode ${ep_no}" $subs_flag $refr_flag "$episode" >/dev/null 2>&1 & + nohup $player_function --no-stdin --mpv-tls-verify=no --mpv-force-media-title="${allanime_title}Episode ${ep_no}" $refr_flag "$episode" >/dev/null 2>&1 & else - nohup $player_function --no-stdin --keep-running --mpv-force-media-title="${allanime_title}Episode ${ep_no}" $subs_flag $refr_flag "$episode" >/dev/null 2>&1 & + nohup $player_function --no-stdin --mpv-tls-verify=no --keep-running --mpv-force-media-title="${allanime_title}Episode ${ep_no}" $refr_flag "$episode" >/dev/null 2>&1 & fi ;; - flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" $subs_flag $refr_flag >/dev/null 2>&1 & ;; - vlc*) nohup $player_function --http-referrer="${allanime_refr}" --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}" $subs_flag $refr_flag >/dev/null 2>&1 & ;; - download) "$player_function" "$episode" "${allanime_title}Episode ${ep_no}" "$subtitle" ;; - catt) nohup catt cast "$episode" -s "$subtitle" >/dev/null 2>&1 & ;; + flatpak_mpv) flatpak run io.mpv.Mpv --tls-verify=no --force-media-title="${allanime_title}Episode ${ep_no}" "$episode" $refr_flag >/dev/null 2>&1 & ;; + vlc*) nohup $player_function --http-referrer="${refr_flag#--referrer=}" --play-and-exit --meta-title="${allanime_title}Episode ${ep_no}" "$episode" >/dev/null 2>&1 & ;; + *yncpla*) nohup $player_function "$episode" -- --tls-verify=no --force-media-title="${allanime_title}Episode ${ep_no}" $refr_flag >/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 @@ -399,11 +480,11 @@ play() { # MAIN # setup -agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0" -allanime_refr="https://allmanga.to" +agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0" +allanime_refr="https://youtu-chan.com" allanime_base="allanime.day" allanime_api="https://api.${allanime_base}" -allanime_key="$(printf '%s' 'SimtVuagFbGR2K7P' | openssl dgst -sha256 -binary | od -A n -t x1 | tr -d ' \n')" +allanime_key="$(printf '%s' 'Xot36i3lK3:v1' | openssl dgst -sha256 -binary | od -A n -t x1 | tr -d ' \n')" mode="${ANI_CLI_MODE:-sub}" download_dir="${ANI_CLI_DOWNLOAD_DIR:-.}" log_episode="${ANI_CLI_LOG:-1}" @@ -421,8 +502,7 @@ exit_after_play="${ANI_CLI_EXIT_AFTER_PLAY:-0}" use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" external_menu_normal_window="${ANI_CLI_EXTERNAL_MENU_NORMAL_WINDOW:-0}" skip_intro="${ANI_CLI_SKIP_INTRO:-0}" -# shellcheck disable=SC2154 -skip_title="$ANI_CLI_SKIP_TITLE" +skip_title="${ANI_CLI_SKIP_TITLE:-}" [ -t 0 ] || (command -v dmenu && use_external_menu=2) [ -t 0 ] || (command -v rofi && use_external_menu=1) hist_dir="${ANI_CLI_HIST_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli}" @@ -430,6 +510,7 @@ hist_dir="${ANI_CLI_HIST_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli}" histfile="$hist_dir/ani-hsts" [ ! -f "$histfile" ] && : >"$histfile" search="${ANI_CLI_DEFAULT_SOURCE:-scrape}" +branch="${ANI_CLI_BRANCH:-master}" while [ $# -gt 0 ]; do case "$1" in @@ -497,7 +578,10 @@ while [ $# -gt 0 ]; do shift ;; -N | --nextep-countdown) search=nextep ;; - -U | --update) update_script ;; + -U | --update) + branch="${2:-$branch}" + update_script + ;; *) query="$(printf "%s" "$query $1" | sed "s|^ ||;s| |+|g")" ;; esac shift @@ -516,12 +600,15 @@ dep_ch "fzf" || true case "$player_function" in debug) ;; download) dep_ch "ffmpeg" "aria2c" ;; - android*) printf "\33[2K\rChecking of players on Android is disabled\n" ;; + android*) printf "\33[2K\rChecking of players on Android is disabled.\nANI-CLI recommends Android MPV for full compatibility.\nAdd:\n include='/storage/emulated/0/mpv/mpv.config.mp4'\nto:\n MPV > Settings > Advanced > mpv.conf\nfor full functionality.\nIgnore, If Done.\n" ;; *iSH*) printf "\33[2K\rChecking of players on iOS is disabled\n" ;; flatpak_mpv) true ;; # handled out of band in where_mpv *) dep_ch "$player_function" ;; esac +# idk where to put this, so I put it here... +trap 'cleanup_shit; exit 1' INT HUP + # searching case "$search" in history) @@ -583,7 +670,10 @@ while cmd=$(printf "next\nreplay\nprevious\nselect\nchange_quality\nquit" | nth new_quality="$(printf "%s" "$links" | launcher | cut -d\> -f1)" select_quality "$new_quality" ;; - *) exit 0 ;; + *) + cleanup_shit + exit 0 + ;; esac [ -z "$ep_no" ] && die "Out of range" play