Skip to content
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ For players you can use the apk (playstore/fdroid) versions of mpv and vlc. Note
pkg install openssl-tool
```

**Important Note:** To get all providers working with android MPV, Please follow below steps:
- Run this command and allow storage permissions:
```sh
termux-setup-storage
```
- Go to MPV > Settings > Advanced > mpv.conf
- add this line:
```txt
include="/storage/emulated/0/mpv/mpv.config.mp4"
```
- Make sure to have storage (photos and videos on newer android) permission allowed to both MPV and termux.
Comment thread
CoolnsX marked this conversation as resolved.
Outdated

</details>

### Tier 2 Support: Windows, WSL, iOS, Steam Deck, FreeBSD
Expand Down
143 changes: 81 additions & 62 deletions ani-cli
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh

version_number="4.14.0"
version_number="4.14.1"

# UI

Expand Down Expand Up @@ -140,9 +140,19 @@ where_mpv() {

# 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 15 -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*)
Expand All @@ -157,22 +167,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
Expand All @@ -181,48 +188,57 @@ 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)
5) provider_init "filemoon" "/Fm-mp4 :/p" ;; # filemoon(m3u8)(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
if [ "$1" = "5" ] && [ -n "$provider_id" ]; then
get_filemoon_links "$provider_id"
else
[ -n "$provider_id" ] && get_links "$provider_id"
fi
[ -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) ;;
*) 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)

# 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"
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"
ct_len=$((file_size - 13 - 16))
plain="$(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)"
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"
printf '%s' "$plain" | tr '{}' '\n' | sed -nE 's|.*"sourceUrl":"--([^"]*)".*"sourceName":"([^"]*)".*|\2 :\1|p'
}

b64url_to_hex() {
Expand Down Expand Up @@ -274,23 +290,13 @@ get_episode_url() {
query_vars="{\"showId\":\"$id\",\"translationType\":\"$mode\",\"episodeString\":\"$ep_no\"}"
query_ext="{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"$query_hash\"}}"

encoded_vars=$(printf '%s' "$query_vars" | sed 's/"/%22/g; s/:/%3A/g; s/{/%7B/g; s/}/%7D/g; s/,/%2C/g')
encoded_ext=$(printf '%s' "$query_ext" | sed 's/"/%22/g; s/:/%3A/g; s/{/%7B/g; s/}/%7D/g; s/,/%2C/g; s/ /%20/g')

api_url="${allanime_api}/api?variables=${encoded_vars}&extensions=${encoded_ext}"

api_resp="$(curl -e "https://youtu-chan.com" -s -A "$agent" -H "Origin: https://youtu-chan.com" "$api_url")"
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

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
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"
Expand Down Expand Up @@ -362,25 +368,39 @@ 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
yt-dlp --referer "$m3u8_refr" "$1" --no-skip-unavailable-fragments --fragment-retries infinite -N 16 -o "$download_dir/$2.mp4"
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"
else
printf "\n\033[1;31mCannot write\033[0m to %s\n" "$file"
printf "\033[1;36mRun:\033[0m termux-setup-storage\n\n"
printf "\033[1;36mThen:\033[0m\n"
printf "> allow storage permission\n"
printf '> add this line: include="%s"\n' "$file"
printf " to MPV > Settings > Advanced > mpv.conf\n"
Comment thread
CoolnsX marked this conversation as resolved.
Outdated
fi
am start --user 0 -a android.intent.action.VIEW -d "$1" -n is.xyz.mpv/.MPVActivity -e "title" "$2" >/dev/null 2>&1
sleep 20 && : >"$file" >/dev/null 2>&1
}

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")"
Expand All @@ -393,30 +413,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
Expand Down Expand Up @@ -459,8 +478,8 @@ 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' 'Xot36i3lK3:v1' | openssl dgst -sha256 -binary | od -A n -t x1 | tr -d ' \n')"
Expand Down
Loading