Skip to content

Commit f659d8c

Browse files
committed
fix: replace anidb with hianime provider and bump version to 5.1.0
1 parent ebb1bdd commit f659d8c

1 file changed

Lines changed: 114 additions & 51 deletions

File tree

ani-cli

Lines changed: 114 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/sh
22

3-
version_number="5.0.3"
3+
version_number="5.1.0"
44

55
# UI
66

@@ -149,49 +149,114 @@ cleanup() {
149149
# SCRAPING
150150

151151
#shellcheck disable=SC2086
152-
anidb_curl() {
152+
hianime_curl() {
153153
$curl_exe -sL -A "$agent" --max-time 10 $cipher_flag "$@"
154154
}
155155

156+
# Decode one base64 value. GNU, BSD and OpenSSL use different switches.
157+
b64_decode() {
158+
printf "%s\n" "$1" | base64 -d 2>/dev/null || printf "%s\n" "$1" | base64 -D 2>/dev/null || printf "%s\n" "$1" | openssl base64 -d -A 2>/dev/null
159+
}
160+
161+
# Deobfuscate the XOR blob with key otaku-embed-v1.
162+
# The subshell keeps the temporary positional parameters out of the caller.
163+
deobfuscate_blob() (
164+
_blob=$1
165+
set -- 111 116 97 107 117 45 101 109 98 101 100 45 118 49
166+
_bytes="$(b64_decode "$_blob" | od -v -An -tu1)"
167+
_output=""
168+
for _byte in $_bytes; do
169+
_key=$1
170+
shift
171+
set -- "$@" "$_key"
172+
_char=$((_byte ^ _key))
173+
_output="${_output}\\$((_char / 64))$(((_char / 8) % 8))$((_char % 8))"
174+
done
175+
printf "%b" "$_output" | sed -n 's|.*"src":"\([^"]*\.m3u8[^"]*\)".*|\1|p'
176+
)
177+
156178
# search the query and give results. format is (id \t name)
157-
anidb_search() {
179+
hianime_search() {
158180
#shellcheck disable=SC2059
159-
_page="$(anidb_curl "$(printf "$search_api" "$1")" | tr '\n' ' ' | sed 's|<a href|\n<a href|g')"
181+
_page="$(hianime_curl "$(printf "$search_api" "$1")")"
160182
if printf "%s" "$_page" | grep -qi "Just a moment"; then
161183
[ "$curl_exe" = "curl" ] && die "Blocked by cloudflare. Try installing curl-impersonate"
162184
die "Blocked by cloudflare."
163185
fi
164-
printf "%s" "$_page" | sed -nE 's|.*anime/([a-z0-9-]+-[0-9]+)".*alt="([^"]+)".*|\1\t\2|p' | sed -e "s|&#039;|'|g" -e 's|&quot;|"|g' -e "s|&amp;|\&|g"
186+
printf "%s" "$_page" | tr '\n' ' ' | sed 's|<div class="film-detail">|\n<div class="film-detail">|g' |
187+
sed -n 's|.*<h3 class="film-name">[[:space:]]*<a href="https://hianime\.at/\([^"]*\)"[[:space:]]*title="\([^"]*\)".*|\1\t\2|p' |
188+
sed -e "s|&#039;|'|g" -e 's|&quot;|"|g' -e "s|&amp;|\&|g" -e "s|&apos;|'|g" -e "s|&lt;|<|g" -e "s|&gt;|>|g"
165189
}
166190

167191
# extract mal_id and seasons metadata
168-
anidb_desc() {
192+
hianime_desc() {
169193
#shellcheck disable=SC2059
170-
_page="$(anidb_curl "$(printf "$desc_api" "$1")" | tr '\n' ' ' | sed 's|<a href|\n<a href|g')"
171-
mal_id="$(printf "%s" "$_page" | sed -nE 's|.*https://myanimelist.net/anime/([0-9]+)/.*|\1|p')"
172-
seasons="$(printf "%s" "$_page" | sed -n '/>Seasons</,/>Details</p' | sed -nE 's|.*anime/([^"]+-[0-9]+)"[^>]*title="([^"]+)".*|\1\t\2|p' | sed -e "s|&#039;|'|g")"
194+
_page="$(hianime_curl "$(printf "$desc_api" "$1")")"
195+
mal_id="$(printf "%s" "$_page" | sed -n 's|.*myanimelist\.net/anime/\([0-9][0-9]*\).*|\1|p')"
196+
# Related entries are grouped in the season list on HiAnime pages.
197+
seasons="$(printf "%s" "$_page" | tr '\n' ' ' | sed 's|<a href|\n<a href|g' | sed -n 's|.*href="https://hianime\.at/\([^"?][^"]*\)"[^>]*title="\([^"]*\)".*|\1\t\2|p' | sed -e "s|&#039;|'|g" -e 's|&quot;|"|g' -e 's|&amp;|\&|g')"
173198
[ -n "$seasons" ] && seasons_option="\nchange_season"
174199
}
175200

176201
# get the episodes list of the selected anime. format is (id \t ep_no)
177-
anidb_episodes() {
202+
hianime_episodes() {
178203
#shellcheck disable=SC2059
179-
_page="$(anidb_curl "$(printf "$episodes_api" "${1##*-}")")"
180-
printf "%s" "$_page" | sed 's|},{|}\n{|g' | sed -nE 's|.*"id":([0-9]+).*"number":([0-9]+).*|\1\t\2|p'
204+
_page="$(hianime_curl -H "X-Requested-With: XMLHttpRequest" "$(printf "$episodes_api" "${1##*-}")")"
205+
printf "%s" "$_page" | sed 's|\\\"|"|g; s|ep-item|\nep-item|g' |
206+
sed -n 's|.*data-number="\([^"]*\)".*data-id="\([0-9][0-9]*\)".*|\2\t\1|p'
181207
}
182208

183209
# extract the individual m3u8 links from response of embed urls
184-
anidb_m3u8() {
185-
_ep_id=$(printf "%s" "$episode_maps" | sed -nE "s|^([0-9]+)\t${1}$|\1|p")
186-
_lang="jpn"
187-
[ "$2" = "dub" ] && _lang="eng"
210+
hianime_m3u8() {
211+
_ep_id=$(printf "%s" "$episode_maps" | sed -n "s|^\([0-9][0-9]*\)\t${1}$|\1|p")
212+
_lang="sub"
213+
[ "$2" = "dub" ] && _lang="dub"
214+
[ -z "$_ep_id" ] && return 1
215+
216+
# 1. Get servers
188217
#shellcheck disable=SC2059
189-
_page="$(anidb_curl "$(printf "$m3u8_api" "${_ep_id}")")"
190-
_embed="$(printf "%s" "$_page" | sed 's|},{|}\n{|g' | sed -nE 's|.*'"${_lang}"'.*embed_url":"([^"]+)".*|\1|p' | sed 's|\\/|/|g' | head -n 1)"
191-
[ -z "$_embed" ] && return 1
192-
_embed_page="$(anidb_curl "$_embed")"
193-
_m3u8_master="$(printf "%s" "$_embed_page" | sed -nE "s|.*file: '([^']*)'.*|\1|p" | head -n 1)"
194-
anidb_curl "${_m3u8_master}" | sed 's|^#EXT-X-STREAM.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|;/EXT-X-I-FRAME/d'
218+
_servers_json="$(hianime_curl -H "X-Requested-With: XMLHttpRequest" "$(printf "$servers_api" "${_ep_id}")")"
219+
220+
# 2. Prefer HD-1; otherwise use the first server for the selected language.
221+
_servers="$(printf "%s" "$_servers_json" | sed 's|\\\"|"|g; s|server-item|\nserver-item|g')"
222+
_hash="$(printf "%s\n" "$_servers" | grep "data-type=\"${_lang}\"" | grep "data-server-name=\"HD-1\"" | sed -n 's|.*data-hash="\([^"]*\)".*|\1|p' | head -n 1)"
223+
[ -z "$_hash" ] && _hash="$(printf "%s\n" "$_servers" | grep "data-type=\"${_lang}\"" | sed -n 's|.*data-hash="\([^"]*\)".*|\1|p' | head -n 1)"
224+
225+
[ -z "$_hash" ] && return 1
226+
227+
# 3. Base64 decode embed URL
228+
_embed_url="$(b64_decode "$_hash")"
229+
[ -z "$_embed_url" ] && return 1
230+
231+
# Extract mal_id for ani-skip
232+
mal_id="$(printf "%s" "$_embed_url" | sed -n 's|.*/stream/mal/\([0-9][0-9]*\)/.*|\1|p')"
233+
234+
# 4. Fetch embed HTML
235+
_embed_page="$(hianime_curl -e "${base_api}/" "$_embed_url")"
236+
_blob="$(printf "%s" "$_embed_page" | sed -n 's|.*window\.__P="\([^"]*\)".*|\1|p')"
237+
[ -z "$_blob" ] && return 1
238+
239+
# 5. Deobfuscate
240+
_m3u8_master="$(deobfuscate_blob "$_blob")"
241+
[ -z "$_m3u8_master" ] && return 1
242+
243+
# 6. Fetch the master playlist and expand quality variants.
244+
_m3u8_content="$(hianime_curl -e "https://zokoanime.video/" "$_m3u8_master")"
245+
if printf "%s" "$_m3u8_content" | grep -q "#EXT-X-STREAM-INF"; then
246+
_base_url="${_m3u8_master%/*}"
247+
_parsed="$(printf "%s\n" "$_m3u8_content" | sed 's|^#EXT-X-STREAM.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|;/EXT-X-I-FRAME/d')"
248+
printf "%s\n" "$_parsed" | while IFS= read -r _line || [ -n "$_line" ]; do
249+
[ -z "$_line" ] && continue
250+
_quality=$(printf "%s" "$_line" | cut -d ">" -f 1 | tr -d " ")
251+
_path=$(printf "%s" "$_line" | cut -d ">" -f 2 | tr -d " ")
252+
case "$_path" in
253+
http*) printf "%s >%s\n" "$_quality" "$_path" ;;
254+
*) printf "%s >%s/%s\n" "$_quality" "$_base_url" "$_path" ;;
255+
esac
256+
done
257+
else
258+
printf "default >%s\n" "$_m3u8_master"
259+
fi
195260
}
196261

197262
# select the quality according to the setting
@@ -208,9 +273,9 @@ select_quality() {
208273

209274
# gets embed url, collects direct links and selects one with desired quality into $episode
210275
get_video_link() {
211-
links=$(anidb_m3u8 "$ep_no" "$mode" | sort -g -r -s)
276+
links=$(hianime_m3u8 "$ep_no" "$mode" | sort -g -r -s)
212277
[ -z "$links" ] && die "No sources found for $mode!"
213-
info "anidb.app links fetched"
278+
info "hianime.at links fetched"
214279
select_quality "$quality"
215280
# should never trigger but just in case
216281
if printf "%s" "$ep_list" | grep -q "^$ep_no$"; then
@@ -265,6 +330,7 @@ time_until_next_ep() {
265330

266331
backup_old_hist() {
267332
backupfile="${histfile}.v4"
333+
: >"${histfile}.new"
268334
while IFS=" " read -r ep_no anime_id anime_title; do
269335
case "$anime_id" in
270336
*-*) printf "%s\t%s\t%s\n" "$ep_no" "$anime_id" "$anime_title" >>"${histfile}.new" ;;
@@ -275,7 +341,7 @@ backup_old_hist() {
275341
}
276342

277343
process_hist_entry() {
278-
ep_list=$(anidb_episodes "$anime_id" | cut -f 2)
344+
ep_list=$(hianime_episodes "$anime_id" | cut -f 2)
279345
ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null
280346
[ -n "$ep_no" ] && printf "%s\t%s - episode %s\n" "$anime_id" "$anime_title" "$ep_no"
281347
}
@@ -297,8 +363,8 @@ update_history() {
297363
# shellcheck disable=SC2086
298364
# $1 = video link, $2 = video name, $3 = extra flags
299365
download() {
300-
command -v "yt-dlp" >/dev/null && yt-dlp "$1" --no-skip-unavailable-fragments --fragment-retries infinite -N 16 -o "$download_dir/$2.mp4" $3 && return 0
301-
ffmpeg -extension_picky 0 -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" $3
366+
command -v "yt-dlp" >/dev/null && yt-dlp --referer "https://zokoanime.video/" "$1" --no-skip-unavailable-fragments --fragment-retries infinite -N 16 -o "$download_dir/$2.mp4" $3 && return 0
367+
ffmpeg -extension_picky 0 -referer "https://zokoanime.video/" -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" $3
302368
}
303369

304370
play_episode() {
@@ -312,27 +378,27 @@ play_episode() {
312378
debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$video_link" ;;
313379
android_mpv) nohup am start --user 0 -a android.intent.action.VIEW -d "$video_link" -n is.xyz.mpv/.MPVActivity -e "title" "${anime_title} Episode ${ep_no}" $player_extra_flags >/dev/null 2>&1 & ;;
314380
android_vlc) nohup am start --user 0 -a android.intent.action.VIEW -d "$video_link" -n org.videolan.vlc/org.videolan.vlc.gui.video.VideoPlayerActivity -e "title" "${anime_title} Episode ${ep_no}" $player_extra_flags >/dev/null 2>&1 & ;;
315-
*flatpak*mpv*) flatpak run io.mpv.Mpv $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & ;;
381+
*flatpak*mpv*) flatpak run io.mpv.Mpv --http-header-fields="Referer: https://zokoanime.video/" $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & ;;
316382
*mpv*)
317383
if [ "$no_detach" = 0 ]; then
318-
nohup $player_function $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 &
384+
nohup $player_function --http-header-fields="Referer: https://zokoanime.video/" $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 &
319385
else
320-
$player_function $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link"
386+
$player_function --http-header-fields="Referer: https://zokoanime.video/" $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link"
321387
mpv_exitcode=$?
322388
[ "$exit_after_play" = 1 ] && [ -z "$_range" ] && exit "$mpv_exitcode"
323389
fi
324390
;;
325391
*iina*)
326392
if pgrep -f "IINA" >/dev/null 2>&1; then
327393
# omit --keep-running when an IINA instance exists to prevent hanging
328-
nohup $player_function $player_extra_flags --no-stdin --mpv-force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 &
394+
nohup $player_function --mpv-http-header-fields="Referer: https://zokoanime.video/" $player_extra_flags --no-stdin --mpv-force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 &
329395
else
330-
nohup $player_function $player_extra_flags --no-stdin --keep-running --mpv-force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 &
396+
nohup $player_function --mpv-http-header-fields="Referer: https://zokoanime.video/" $player_extra_flags --no-stdin --keep-running --mpv-force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 &
331397
fi
332398
;;
333-
*vlc*) nohup $player_function $player_extra_flags --play-and-exit --meta-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & ;;
334-
*yncpla*) nohup $player_function $player_extra_flags "$video_link" -- --force-media-title="${anime_title} Episode ${ep_no}" >/dev/null 2>&1 & ;;
335-
download) "$player_function" "$video_link" "${anime_title} Episode ${ep_no}" $player_extra_flags ;;
399+
*vlc*) nohup $player_function --http-referrer="https://zokoanime.video/" $player_extra_flags --play-and-exit --meta-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & ;;
400+
*yncpla*) nohup $player_function $player_extra_flags "$video_link" -- --http-header-fields="Referer: https://zokoanime.video/" --force-media-title="${anime_title} Episode ${ep_no}" >/dev/null 2>&1 & ;;
401+
download) "$player_function" "$video_link" "${anime_title} Episode ${ep_no}" "$player_extra_flags" ;;
336402
catt) nohup catt cast $player_extra_flags "$video_link" >/dev/null 2>&1 & ;;
337403
iSH)
338404
printf "\033]8;;vlc://%s\a~~~~~~~~~~~~~~~~~~~~\n~ Tap to open VLC ~\n~~~~~~~~~~~~~~~~~~~~\033]8;;\a\n" "$video_link"
@@ -379,13 +445,11 @@ play() {
379445
# MAIN
380446

381447
# setup
382-
base_api="https://anidb.app"
383-
search_api="${base_api}/browse?q=%s"
384-
# search_api_alt="${base_api}/search/suggestions?q=%s"
385-
desc_api="${base_api}/anime/%s"
386-
episodes_api="${base_api}/api/frontend/anime/%s/episodes"
387-
m3u8_api="${base_api}/api/frontend/episode/%s/languages"
388-
448+
base_api="https://hianime.at"
449+
search_api="${base_api}/search?keyword=%s"
450+
desc_api="${base_api}/%s"
451+
episodes_api="${base_api}/api/theme/episode/list/%s"
452+
servers_api="${base_api}/api/theme/episode/servers?episodeId=%s"
389453
ciphers='ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305'
390454
tls13_ciphers='TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256'
391455
agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
@@ -527,15 +591,14 @@ trap 'cleanup; exit 1' INT HUP TERM
527591
case "$source" in
528592
history)
529593
backup_old_hist
530-
anime_list=$(while read -r ep_no anime_id anime_title; do process_hist_entry & done <"$histfile")
531-
wait
594+
anime_list=$(while read -r ep_no anime_id anime_title; do process_hist_entry & done <"$histfile"; wait)
532595
[ -z "$anime_list" ] && die "No unwatched series in history!"
533596
[ -z "${index##*[!0-9]*}" ] && anime_id=$(printf "%s" "$anime_list" | nl -w 2 | sed 's/^[[:space:]]//' | nth "Select anime: " | cut -f 1)
534597
[ -z "${index##*[!0-9]*}" ] || anime_id=$(printf "%s" "$anime_list" | sed -n "${index}p" | cut -f 1)
535598
[ -z "$anime_id" ] && exit 1
536599
anime_title=$(printf "%s" "$anime_list" | grep "^$anime_id " | cut -f 2 | sed 's| - episode.*||')
537-
anidb_desc "$anime_id" || true
538-
episode_maps="$(anidb_episodes "$anime_id")"
600+
hianime_desc "$anime_id" || true
601+
episode_maps="$(hianime_episodes "$anime_id")"
539602
ep_list="$(printf "%s" "$episode_maps" | cut -f 2)"
540603
ep_no=$(printf "%s" "$anime_list" | grep "^$anime_id " | sed -nE 's/.*- episode (.+)$/\1/p')
541604
;;
@@ -553,15 +616,15 @@ case "$source" in
553616

554617
query=$(printf "%s" "$query" | sed 's| |+|g')
555618

556-
anime_list=$(anidb_search "$query")
619+
anime_list=$(hianime_search "$query")
557620
[ -z "$anime_list" ] && die "No results found!"
558621
[ "$index" -eq "$index" ] 2>/dev/null && result=$(printf "%s" "$anime_list" | sed -n "${index}p")
559622
[ -z "$index" ] && result=$(printf "%s" "$anime_list" | nl -w 2 | sed 's/^[[:space:]]//' | nth "Select anime: ")
560623
[ -z "$result" ] && die "Invalid anime selection"
561624
anime_title="$(printf "%s" "$result" | cut -f 2)"
562625
anime_id="$(printf "%s" "$result" | cut -f 1)"
563-
anidb_desc "$anime_id" || true
564-
episode_maps="$(anidb_episodes "$anime_id")"
626+
hianime_desc "$anime_id" || true
627+
episode_maps="$(hianime_episodes "$anime_id")"
565628
ep_list="$(printf '%s' "$episode_maps" | cut -f 2)"
566629
[ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$menu_multi_flag")
567630
[ -z "$ep_no" ] && die "Invalid episode selection"
@@ -589,8 +652,8 @@ while cmd=$(printf "next\nreplay\nprevious\nselect$seasons_option\nchange_qualit
589652
[ -z "$new_anime" ] && die "Invalid season selection"
590653
anime_title="$(printf "%s" "$new_anime" | cut -f 2)"
591654
anime_id="$(printf "%s" "$new_anime" | cut -f 1)"
592-
anidb_desc "$anime_id" || true
593-
episode_maps="$(anidb_episodes "$anime_id")"
655+
hianime_desc "$anime_id" || true
656+
episode_maps="$(hianime_episodes "$anime_id")"
594657
ep_list="$(printf '%s' "$episode_maps" | cut -f 2)"
595658
ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$menu_multi_flag")
596659
[ -z "$ep_no" ] && die "Invalid episode selection"

0 commit comments

Comments
 (0)