diff --git a/README.md b/README.md index e0304dbde..88be4f26d 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@

-A cli to browse and watch anime (alone AND with friends). This tool scrapes the site anidb. +A cli to browse and watch anime (alone AND with friends). This tool scrapes the site hianime.

@@ -534,12 +534,12 @@ Ani-skip uses the external lua script function of mpv and as such – for now ## FAQ
-* Can I change subtitle language or turn them off? - No, the subtitles are baked into the video. +* Can I change subtitle language or turn them off? - Subtitles are a separate english track handed to the player, so you can toggle them there (mpv: press `v`). Other languages are not offered. * Can I watch dub? - Yes, use `--dub`. * Can I change dub language? - No. * Can I change media source? - No (unless you can scrape that source yourself). * Can I use vlc? - Yes, use `--vlc` or `export ANI_CLI_PLAYER=vlc`. -* Can I adjust resolution? - Yes, use `-q resolution`, for example `ani-cli -q 1080`. +* Can I adjust resolution? - Yes, use `-q resolution`, for example `ani-cli -q 1080`. The provider currently serves a single 1080p stream, other values fall back to best. * How can I download? - Use `-d`, it will download into your working directory. * Can i change download folder? - Yes, set the `ANI_CLI_DOWNLOAD_DIR` to your desired location. * How can I bulk download? - `Use -d -e firstepisode-lastepisode`, for example `ani-cli onepiece -d -e 1-1000`. diff --git a/ani-cli b/ani-cli index 57f03100b..06f41d847 100755 --- a/ani-cli +++ b/ani-cli @@ -1,6 +1,6 @@ #!/bin/sh -version_number="5.0.4" +version_number="5.1.0" # UI @@ -149,60 +149,120 @@ cleanup() { # SCRAPING #shellcheck disable=SC2086 -anidb_curl() { - _response="$($curl_exe -sL -A "$agent" --max-time 10 $cipher_flag -w ' %{http_code}' "$@")" || - die "Connection error: could not fetch $1 (curl exit $?)" +hianime_curl() { + _url="" + for _arg; do + case "$_arg" in http*) _url="$_arg" ;; *) ;; esac + done + [ -z "$_url" ] && _url="$1" + _response="$($curl_exe -sL -A "$agent" --max-time 15 $cipher_flag -w ' %{http_code}' "$@")" || + die "Connection error: could not fetch $_url (curl exit $?)" _http_code=${_response##* } _response=${_response% *} case "$_http_code" in 2??) ;; - *) printf "%s" "$_response" | grep -qi "Just a moment" || die "Request failed: HTTP $_http_code from $1" ;; + *) printf "%s" "$_response" | grep -qi "Just a moment" || die "Request failed: HTTP $_http_code from $_url" ;; esac printf "%s" "$_response" } +# Decode one base64 value. GNU, BSD and OpenSSL use different switches. +b64_decode() { + 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 +} + +# The embed page ships its config as base64(json XOR "otaku-embed-v1"), this prints the json. +# Runs in a subshell so the key bytes can live in the positional parameters and rotate with shift. +deobfuscate_blob() ( + _bytes="$(b64_decode "$1" | od -v -An -tu1)" + set -- 111 116 97 107 117 45 101 109 98 101 100 45 118 49 + _output="" + for _byte in $_bytes; do + _char=$((_byte ^ $1)) + _output="${_output}\\0$((_char / 64))$(((_char / 8) % 8))$((_char % 8))" + set -- "$@" "$1" + shift + done + printf "%b" "$_output" +) + # search the query and give results. format is (id \t name) -anidb_search() { +hianime_search() { #shellcheck disable=SC2059 - _page="$(anidb_curl "$(printf "$search_api" "$1")")" || return 1 - _page="$(printf "%s" "$_page" | tr '\n' ' ' | sed 's|Just a moment"; then [ "$curl_exe" = "curl" ] && die "Blocked by cloudflare. Try installing curl-impersonate" die "Blocked by cloudflare." fi - printf "%s" "$_page" | sed -nE 's|.*anime/([a-z0-9-]+-[0-9]+)".*alt="([^"]+)".*|\1\t\2|p' | sed -e "s|'|'|g" -e 's|"|"|g' -e "s|&|\&|g" -} - -# extract mal_id and seasons metadata -anidb_desc() { - #shellcheck disable=SC2059 - _page="$(anidb_curl "$(printf "$desc_api" "$1")" | tr '\n' ' ' | sed 's|SeasonsDetails]*title="([^"]+)".*|\1\t\2|p' | sed -e "s|'|'|g")" - [ -n "$seasons" ] && seasons_option="\nchange_season" + # the top 10 sidebar repeats the result markup, cut it off before flattening the page + printf "%s" "$_page" | sed '/id="main-sidebar"/,$d' | tr '\n' ' ' | sed 's|
|\n
|g' | + sed -nE 's|.*

[[:space:]]*|g" } # get the episodes list of the selected anime. format is (id \t ep_no) -anidb_episodes() { +hianime_episodes() { #shellcheck disable=SC2059 - _page="$(anidb_curl "$(printf "$episodes_api" "${1##*-}")")" || return 1 - [ -z "$_page" ] && die "Connection error: no response from $base_api" - printf "%s" "$_page" | sed 's|},{|}\n{|g' | sed -nE 's|.*"id":([0-9]+).*"number":([0-9]+).*|\1\t\2|p' + _page="$(hianime_curl "$(printf "$episodes_api" "${1##*-}")")" || return 1 + # ids from the v5.0 provider have the same shape, so check the slug in the episode links too + printf "%s" "$_page" | sed 's|\\||g; s|ep-item|\nep-item|g' | + sed -nE "s|.*data-number=\"([^\"]*)\".*data-id=\"([0-9]+)\".*/watch/${1}[?]ep=.*|\\2\\t\\1|p" } -# extract the individual m3u8 links from response of embed urls -anidb_m3u8() { +# resolves episode $1 in mode $2 (sub/dub): sets links (quality >url per line), sub_link, mal_id and refr +hianime_m3u8() { _ep_id=$(printf "%s" "$episode_maps" | sed -nE "s|^([0-9]+)\t${1}$|\1|p") - _lang="jpn" - [ "$2" = "dub" ] && _lang="eng" #shellcheck disable=SC2059 - _page="$(anidb_curl "$(printf "$m3u8_api" "${_ep_id}")")" - _embed="$(printf "%s" "$_page" | sed 's|},{|}\n{|g' | sed -nE 's|.*'"${_lang}"'.*embed_url":"([^"]+)".*|\1|p' | sed 's|\\/|/|g' | head -n 1)" + _servers="$(hianime_curl "$(printf "$servers_api" "$_ep_id")" | sed 's|\\"|"|g; s|server-item|\nserver-item|g')" + # prefer ZokoAnime; fallback to any server whose embed contains zokoanime, then HD-1 + _hash="$(printf "%s\n" "$_servers" | sed -nE 's|.*data-type="'"$2"'".*data-server-name="ZokoAnime".*data-hash="([^"]*)".*|\1|p' | head -n 1)" + if [ -z "$_hash" ]; then + for _h in $(printf "%s\n" "$_servers" | sed -nE 's|.*data-type="'"$2"'".*data-hash="([^"]*)".*|\1|p'); do + case "$(b64_decode "$_h")" in + *zokoanime*) + _hash="$_h" + break + ;; + *) ;; + esac + done + fi + [ -z "$_hash" ] && _hash="$(printf "%s\n" "$_servers" | sed -nE 's|.*data-type="'"$2"'".*data-server-name="HD-1".*data-hash="([^"]*)".*|\1|p' | head -n 1)" + [ -z "$_hash" ] && _hash="$(printf "%s\n" "$_servers" | sed -nE 's|.*data-type="'"$2"'".*data-hash="([^"]*)".*|\1|p' | head -n 1)" + _embed="$(b64_decode "$_hash")" [ -z "$_embed" ] && return 1 - _embed_page="$(anidb_curl "$_embed")" - _m3u8_master="$(printf "%s" "$_embed_page" | sed -nE "s|.*file: '([^']*)'.*|\1|p" | head -n 1)" - anidb_curl "${_m3u8_master}" | sed 's|^#EXT-X-STREAM.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|;/EXT-X-I-FRAME/d' + # the stream host wants the embed site as referer, and the embed url carries the mal id for ani-skip + refr="${_embed%%://*}:" + _tmp_host="${_embed#*://}" + refr="${refr}//${_tmp_host%%/*}/" + _tmp_mal="${_embed#*/mal/}" + if [ "$_tmp_mal" != "$_embed" ]; then + mal_id="${_tmp_mal%%/*}" + else + mal_id="" + fi + _blob="$(hianime_curl "$_embed" | sed -nE 's|.*window\.__P="([^"]*)".*|\1|p')" + [ -z "$_blob" ] && return 1 + _json="$(deobfuscate_blob "$_blob")" + _tmp_src="${_json#*\"src\":\"}" + _m3u8_master="${_tmp_src%%\"*}" + case "$_m3u8_master" in + *.m3u8*) ;; + *) _m3u8_master="" ;; + esac + [ -z "$_m3u8_master" ] && return 1 + _sub_entries="$(printf "%s" "$_json" | tr '}' '\n' | sed -n 's|.*"lang":"\([^"]*\)".*"src":"\([^"]*\.vtt[^"]*\)".*|\1\t\2|p')" + _sub_langs="${ANI_CLI_SUB_LANG:-en}" + _sub_pattern="$(printf "%s" "$_sub_langs" | tr ',:' '|')" + sub_link="$(printf "%s\n" "$_sub_entries" | grep -iE "^(${_sub_pattern})[[:space:]]" | head -n 1 | cut -f 2)" + [ -z "$sub_link" ] && sub_link="$(printf "%s\n" "$_sub_entries" | head -n 1 | cut -f 2)" + _m3u8_content="$(hianime_curl "$_m3u8_master" -e "$refr")" + if printf "%s" "$_m3u8_content" | grep -q "#EXT-X-STREAM-INF"; then + links="$(printf "%s\n" "$_m3u8_content" | sed 's|^#EXT-X-STREAM.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|;/EXT-X-I-FRAME/d' | sed "\|>https*://|!s|>|>${_m3u8_master%/*}/|" | sort -g -r -s)" + else + links="default >$_m3u8_master" + fi + [ -n "$links" ] } # select the quality according to the setting @@ -214,17 +274,16 @@ select_quality() { esac [ -z "$_result" ] && printf "\33[2K\r\033[1;33m%s\033[0m\n" "$1 not found, defaulting to best" >&2 && _result=$(printf "%s" "$links" | head -n 1) - video_link=$(printf "%s" "$_result" | cut -d ">" -f 2) + video_link="${_result#*>}" } # gets embed url, collects direct links and selects one with desired quality into $episode get_video_link() { - links=$(anidb_m3u8 "$ep_no" "$mode" | sort -g -r -s) - [ -z "$links" ] && die "No sources found for $mode!" - info "anidb.app links fetched" + hianime_m3u8 "$ep_no" "$mode" || die "No sources found for $mode!" + info "hianime.at links fetched" select_quality "$quality" # should never trigger but just in case - if printf "%s" "$ep_list" | grep -q "^$ep_no$"; then + if printf "%s\n" "$ep_list" | grep -Fqx "$ep_no"; then [ -z "$video_link" ] && die "Episode is released, but no valid sources!" else [ -z "$video_link" ] && die "Episode not released!" @@ -276,6 +335,7 @@ time_until_next_ep() { backup_old_hist() { backupfile="${histfile}.v4" + : >"${histfile}.new" while IFS=" " read -r ep_no anime_id anime_title; do case "$anime_id" in *-*) printf "%s\t%s\t%s\n" "$ep_no" "$anime_id" "$anime_title" >>"${histfile}.new" ;; @@ -286,9 +346,12 @@ backup_old_hist() { } process_hist_entry() { - ep_list=$(anidb_episodes "$anime_id" | cut -f 2) - 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" "$anime_id" "$anime_title" "$ep_no" + _entry_ep="$1" + _entry_id="$2" + _entry_title="$3" + _entry_list=$(hianime_episodes "$_entry_id" | cut -f 2) + _next_ep=$(printf "%s" "$_entry_list" | sed -n "/^${_entry_ep}$/{n;p;}") 2>/dev/null + [ -n "$_next_ep" ] && printf "%s\t%s - episode %s\n" "$_entry_id" "$_entry_title" "$_next_ep" } update_history() { @@ -308,41 +371,44 @@ update_history() { # shellcheck disable=SC2086 # $1 = video link, $2 = video name, $3 = extra flags download() { - 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 - ffmpeg -extension_picky 0 -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" $3 + [ -n "$sub_link" ] && $curl_exe -sL -A "$agent" --max-time 10 $cipher_flag -e "$refr" "$sub_link" -o "$download_dir/$2.vtt" 2>/dev/null & + command -v "yt-dlp" >/dev/null && yt-dlp --referer "$refr" "$1" --no-skip-unavailable-fragments --fragment-retries infinite -N 16 -o "$download_dir/$2.mp4" $3 && return 0 + ffmpeg -extension_picky 0 -referer "$refr" -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" $3 } play_episode() { [ "$log_episode" = 1 ] && [ "$player_function" != "debug" ] && [ "$player_function" != "download" ] && command -v logger >/dev/null && logger -t ani-cli "${anime_title} ${ep_no}" - [ "$skip_intro" = 1 ] && skip_flag="$(ani-skip -i "$mal_id" -e "$canon_ep_no")" [ -z "$video_link" ] && get_video_link + [ "$skip_intro" = 1 ] && skip_flag="$(ani-skip -i "$mal_id" -e "$canon_ep_no")" # wait for the previous player to close (in range selection) wait # shellcheck disable=SC2086 case "$player_function" in - debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$video_link" ;; + debug) printf "All links:\n%s\nSelected link:\n%s\nSubtitles:\n%s\n" "$links" "$video_link" "$sub_link" ;; 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 & ;; 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 & ;; - *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 & ;; + *flatpak*mpv*) flatpak run io.mpv.Mpv --referrer="$refr" ${sub_link:+--sub-file="$sub_link"} $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & ;; *mpv*) if [ "$no_detach" = 0 ]; then - nohup $player_function $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & + nohup $player_function --referrer="$refr" ${sub_link:+--sub-file="$sub_link"} $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & else - $player_function $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" + $player_function --referrer="$refr" ${sub_link:+--sub-file="$sub_link"} $skip_flag $player_extra_flags --force-media-title="${anime_title} Episode ${ep_no}" "$video_link" mpv_exitcode=$? [ "$exit_after_play" = 1 ] && [ -z "$_range" ] && exit "$mpv_exitcode" fi ;; *iina*) + # mpv splits sub-files on ':', so the url has to be escaped for iina + _sub_iina="$(printf "%s" "$sub_link" | sed 's|:|\\:|g')" if pgrep -f "IINA" >/dev/null 2>&1; then # omit --keep-running when an IINA instance exists to prevent hanging - nohup $player_function $player_extra_flags --no-stdin --mpv-force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & + nohup $player_function --mpv-referrer="$refr" ${_sub_iina:+--mpv-sub-files="$_sub_iina"} $player_extra_flags --no-stdin --mpv-force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & else - 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 & + nohup $player_function --mpv-referrer="$refr" ${_sub_iina:+--mpv-sub-files="$_sub_iina"} $player_extra_flags --no-stdin --keep-running --mpv-force-media-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & fi ;; - *vlc*) nohup $player_function $player_extra_flags --play-and-exit --meta-title="${anime_title} Episode ${ep_no}" "$video_link" >/dev/null 2>&1 & ;; - *yncpla*) nohup $player_function $player_extra_flags "$video_link" -- --force-media-title="${anime_title} Episode ${ep_no}" >/dev/null 2>&1 & ;; + *vlc*) nohup $player_function --http-referrer="$refr" $player_extra_flags --play-and-exit --meta-title="${anime_title} Episode ${ep_no}" "$video_link" ${sub_link:+:input-slave="$sub_link"} >/dev/null 2>&1 & ;; + *yncpla*) nohup $player_function $player_extra_flags "$video_link" -- --referrer="$refr" ${sub_link:+--sub-file="$sub_link"} $skip_flag --force-media-title="${anime_title} Episode ${ep_no}" >/dev/null 2>&1 & ;; download) "$player_function" "$video_link" "${anime_title} Episode ${ep_no}" $player_extra_flags ;; catt) nohup catt cast $player_extra_flags "$video_link" >/dev/null 2>&1 & ;; iSH) @@ -352,7 +418,9 @@ play_episode() { *) nohup $player_function $player_extra_flags "$video_link" >/dev/null 2>&1 & ;; esac replay="$video_link" + replay_sub="$sub_link" unset video_link + unset sub_link update_history [ "$menu_program" != "fzf" ] && wait } @@ -373,14 +441,14 @@ play() { for i in $_range; do tput clear ep_no=$i - canon_ep_no="$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/=")" + canon_ep_no="$(printf "%s\n" "$ep_list" | grep -Fnx "$ep_no" | cut -d: -f1)" info "Playing episode $ep_no..." [ "$i" = "$_end" ] && unset _range play_episode done else - printf "%s" "$ep_list" | grep -q "^$ep_no$" || die "Invalid episode!" - canon_ep_no="$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/=")" + printf "%s\n" "$ep_list" | grep -Fqx "$ep_no" || die "Invalid episode!" + canon_ep_no="$(printf "%s\n" "$ep_list" | grep -Fnx "$ep_no" | cut -d: -f1)" play_episode fi # moves up to stored position and deletes to end @@ -390,13 +458,10 @@ play() { # MAIN # setup -base_api="https://anidb.app" -search_api="${base_api}/browse?q=%s" -# search_api_alt="${base_api}/search/suggestions?q=%s" -desc_api="${base_api}/anime/%s" -episodes_api="${base_api}/api/frontend/anime/%s/episodes" -m3u8_api="${base_api}/api/frontend/episode/%s/languages" - +base_api="https://hianime.at" +search_api="${base_api}/search?keyword=%s" +episodes_api="${base_api}/api/theme/episode/list/%s" +servers_api="${base_api}/api/theme/episode/servers?episodeId=%s" 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' tls13_ciphers='TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256' agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" @@ -538,15 +603,16 @@ trap 'cleanup; exit 1' INT HUP TERM case "$source" in history) backup_old_hist - anime_list=$(while read -r ep_no anime_id anime_title; do process_hist_entry & done <"$histfile") - wait + anime_list=$( + while read -r ep_no anime_id anime_title; do process_hist_entry "$ep_no" "$anime_id" "$anime_title" & done <"$histfile" + wait + ) [ -z "$anime_list" ] && die "No unwatched series in history!" [ -z "${index##*[!0-9]*}" ] && anime_id=$(printf "%s" "$anime_list" | nl -w 2 | sed 's/^[[:space:]]//' | nth "Select anime: " | cut -f 1) [ -z "${index##*[!0-9]*}" ] || anime_id=$(printf "%s" "$anime_list" | sed -n "${index}p" | cut -f 1) [ -z "$anime_id" ] && exit 1 anime_title=$(printf "%s" "$anime_list" | grep "^$anime_id " | cut -f 2 | sed 's| - episode.*||') - anidb_desc "$anime_id" || true - episode_maps="$(anidb_episodes "$anime_id")" || exit 1 + episode_maps="$(hianime_episodes "$anime_id")" || exit 1 ep_list="$(printf "%s" "$episode_maps" | cut -f 2)" ep_no=$(printf "%s" "$anime_list" | grep "^$anime_id " | sed -nE 's/.*- episode (.+)$/\1/p') ;; @@ -564,15 +630,14 @@ case "$source" in query=$(printf "%s" "$query" | sed 's| |+|g') - anime_list=$(anidb_search "$query") || exit 1 + anime_list=$(hianime_search "$query") || exit 1 [ -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" ] && die "Invalid anime selection" anime_title="$(printf "%s" "$result" | cut -f 2)" anime_id="$(printf "%s" "$result" | cut -f 1)" - anidb_desc "$anime_id" || true - episode_maps="$(anidb_episodes "$anime_id")" || exit 1 + episode_maps="$(hianime_episodes "$anime_id")" || exit 1 ep_list="$(printf '%s' "$episode_maps" | cut -f 2)" [ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$menu_multi_flag") [ -z "$ep_no" ] && die "Invalid episode selection" @@ -588,20 +653,23 @@ tput sc play [ "$player_function" = "download" ] || [ "$player_function" = "debug" ] && exit 0 -#shellcheck disable=SC2059 -while cmd=$(printf "next\nreplay\nprevious\nselect$seasons_option\nchange_quality\nquit" | nth "Playing episode $ep_no of $anime_title... "); do +while cmd=$(printf "next\nreplay\nprevious\nselect\nchange_season\nchange_quality\nquit" | nth "Playing episode $ep_no of $anime_title... "); do case "$cmd" in next) ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null ;; - replay) video_link="$replay" ;; + replay) + video_link="$replay" + sub_link="$replay_sub" + ;; 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: " "$menu_multi_flag") ;; change_season) - new_anime="$(printf "%s" "$seasons" | nl -w 2 | sed 's/^[[:space:]]//' | nth "Select season: ")" + # hianime has no season list, related entries are found by searching the title + _query=$(printf "%s" "$anime_title" | sed 's| |+|g; s|&|%26|g; s|#|%23|g') + new_anime="$(hianime_search "$_query" | nl -w 2 | sed 's/^[[:space:]]//' | nth "Select season: ")" [ -z "$new_anime" ] && die "Invalid season selection" anime_title="$(printf "%s" "$new_anime" | cut -f 2)" anime_id="$(printf "%s" "$new_anime" | cut -f 1)" - anidb_desc "$anime_id" || true - episode_maps="$(anidb_episodes "$anime_id")" || exit 1 + episode_maps="$(hianime_episodes "$anime_id")" || exit 1 ep_list="$(printf '%s' "$episode_maps" | cut -f 2)" ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$menu_multi_flag") [ -z "$ep_no" ] && die "Invalid episode selection" diff --git a/ani-cli.1 b/ani-cli.1 index 450bdde01..1b770ada2 100644 --- a/ani-cli.1 +++ b/ani-cli.1 @@ -9,7 +9,7 @@ A shell script to browse and search anime from the command-line. .PD 0 .P .PD -This tool scrapes the site anidb. +This tool scrapes the site hianime. .PD 0 .P .PD @@ -34,8 +34,8 @@ Show logs. \fB\-h | --help\fR Show summary of options. .TP -\fB\-q | --quality\fR \fI\,\/\fR -Set the video quality. Default quality is best. +\fB\-q | --quality\fR \fI\,\/\fR +Set the video quality. Default quality is best. The provider currently serves a single 1080p stream, other values fall back to best. .TP \fB\-s | --syncplay\fR Watch anime together with friends, using Syncplay (works with mpv only). @@ -85,7 +85,7 @@ Controls the scraped media's mode, valid options are sub or dub. Default is sub. Controls the directory where files are downloaded. Default is the current dir. .TP \fBANI_CLI_QUALITY\fR -Controls the scraped media's quality, check anidb for valid options or set to worst/best. Default is best. +Controls the scraped media's quality, a resolution such as 1080, or worst/best. Default is best. .TP \fBANI_CLI_PLAYER\fR Sets the player ani-cli uses. Can be debug (print links), download (equivalent to -d), android_mpv (apk and am start), android_vlc (apk and am start), catt (for streaming to tv), or any player that can play urls. For defaults see working without arguments. diff --git a/disclaimer.md b/disclaimer.md index de35faf3d..face5590d 100644 --- a/disclaimer.md +++ b/disclaimer.md @@ -33,7 +33,7 @@ This project has no control on the content it is serving, using copyrighted cont A browser is a tool, and the maliciousness of the tool is directly based on the user. -This project uses client-side content access mechanisms. Hence, the copyright infrigements or DMCA in this project's regards are to be forwarded to the associated site by the associated notifier of any such claims. As of writing this is [anidb.app](https://anidb.app/) +This project uses client-side content access mechanisms. Hence, the copyright infrigements or DMCA in this project's regards are to be forwarded to the associated site by the associated notifier of any such claims. As of writing this is [hianime.at](https://hianime.at/) Do not harass the maintainer. diff --git a/hacking.md b/hacking.md index e291a2e4e..46ff13fdb 100644 --- a/hacking.md +++ b/hacking.md @@ -1,5 +1,5 @@ # Hacking ani-cli -Ani-cli is set up to scrape one platform - currently allanime. Supporting multiple sources at a time would require more changes than we (the maintainers) find worth doing, for this reason any feature request asking for a new site is rejected. +Ani-cli is set up to scrape one platform - currently hianime. Supporting multiple sources at a time would require more changes than we (the maintainers) find worth doing, for this reason any feature request asking for a new site is rejected. However ani-cli being open-source and the pirate anime streaming sites being so similar you can hack ani-cli to support any site that follows a few conventions.