pystardust / ani-cli

A cli tool to browse and play anime
GNU General Public License v3.0
7.29k stars 528 forks source link

No such file or directory. #1336

Closed DhruvSinha2003 closed 1 month ago

DhruvSinha2003 commented 1 month ago

After installing ani-cli and all the dependencies on my Windows 11 machine using scoop, I get this error: C:\Users\dhruv>ani-cli /bin/bash: C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli: No such file or directory

I have reinstalled ani-cli many times and also removed all the scoop files and installed that again.

When I run ani-cli on powershell, i get this error: PowerShell 7.4.2 PS C:\Users\dhruv> ani-cli /bin/bash: C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli: No such file or directory PS C:\Users\dhruv>

When I run ani-cli on git bash: dhruv@DhruvSinha MINGW64 ~ $ ani-cli Checking dependencies... Search anime: naruto

but it gets stuck here no matter what you enter in the search anime prompt

Only way i got it to work is by going in the scoop shims folder and manually running the ani-cli Windows Command Script

this is what is see in C:\Users\dhruv\scoop\apps\ani-cli\current: image

ykhan21 commented 1 month ago
  1. In powershell or cmd, what happens when you run ani-cli.cmd?
  2. In git bash, what do you mean by stuck? Can you share a screenshot of what happens after you type naruto Enter?
  3. In git bash, what is the output of curl --version?
DhruvSinha2003 commented 1 month ago
  1. In powershell or cmd, what happens when you run ani-cli.cmd?
  2. In git bash, what do you mean by stuck? Can you share a screenshot of what happens after you type naruto Enter?
  3. In git bash, what is the output of curl --version?

Thanks for the reply

1. image

2. image I have pressed enter several times in the git bash above... waiting for a while does not resolve anything either..

3. image

ykhan21 commented 1 month ago

It looks like there are two problems, one in powershell and one in git bash.

  1. Could you paste your scoop/shims/ani-cli.cmd file here? Open it with notepad and paste the contents here.

  2. If the ani-cli.cmd file's second like begins with @bash, it might be using wsl. You need to make it use Git's bash. Replace @bash with: @"C:\Users\dhruv\scoop\apps\git\current\bin\bash.exe" (if you installed git with scoop) or @"C:\Program Files\Git\bin\bash.exe" (if you installed git manually).

  3. Check if the fix in (2) works in powershell.

  4. I don't know what's going on with the git bash problem. Could it be a readline problem? In Git Bash, could you try running read -r query, then enter "naruto Enter", then print the query variable. image image

ykhan21 commented 1 month ago

Also, make sure git for windows is up to date.

DhruvSinha2003 commented 1 month ago

ani-cli.cmd: @ rem C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli @ bash "C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli" %*

i added the space after the "@" because it was tagging people

After replacing: @ rem C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli @"C:\Users\dhruv\scoop\apps\git\current\bin\bash.exe" "C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli" %*

It is working in powershell now!!

I don't need it to work in git bash anymore but just for the sake of understanding the problem:

image

Thank you for your help... I am cool with closing this issue unless you want to debug the bash problem....

ykhan21 commented 1 month ago

It works when you run ani-cli in powershell, right? You shouldn't need to add the .cmd every time. Also, please keep ani-cli up to date with ani-cli -U instead of scoop update ani-cli until the scoop install script for ani-cli is fixed here.

(Just to note, others on Windows 11 have had trouble with wsl hijacking the script.)

Can you try updating ani-cli in powershell and see if it works in git bash?

DhruvSinha2003 commented 1 month ago

It does work with just ani-cli, I am not using ani-cli.cmd

image :(

ykhan21 commented 1 month ago

Did you edit the ani-cli script? It should work. It works for me on Win11 + pwsh. Can you paste the .rej reject file in C:\Users\dhruv\scoop\apps\ani-cli\current\?

Try reinstalling ani-cli:

  1. scoop uninstall ani-cli
  2. scoop install ani-cli.
  3. Replace @bash with @"C:\Users\dhruv\scoop\apps\git\current\bin\bash.exe" in the ani-cli.cmd file.
  4. Update ani-cli: ani-cli -U.
DhruvSinha2003 commented 1 month ago
ani-cli.rej --- ani-cli 2024-05-18 18:49:24.438424100 +0530 +++ - 2024-05-20 19:42:34.341551800 +0530 @@ -1,508 +1,508 @@ -#!/bin/sh - -version_number="4.8.6" - -# 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) - --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 - *repackager.wixmp.com*) - extract_link=$(printf "%s" "$episode_link" | cut -d'>' -f2 | sed 's|repackager.wixmp.com/||g;s|\.urlset.*||g') - for j in $(printf "%s" "$episode_link" | sed -nE 's|.*/,([^/]*),/mp4.*|\1|p' | sed 's|,|\ -|g'); do - printf "%s >%s\n" "$j" "$extract_link" | sed "s|,[^/]*|${j}|g" - done | sort -nr - ;; - *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 "wixmp" "/Default :/p" ;; # wixmp(default)(m3u8)(multi) -> (mp4)(multi) - 2) provider_init "dropbox" "/Sak :/p" ;; # dropbox(mp4)(single) - 3) provider_init "wetransfer" "/Kir :/p" ;; # wetransfer(mp4)(single) - 4) 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 5" - 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" - 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}" -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 ;; - --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 <http://www.gnu.org/licenses/>. -# -# Project repository: https://github.com/pystardust/ani-cli +#!/bin/sh + +version_number="4.8.6" + +# 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) + --skip-title <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 + *repackager.wixmp.com*) + extract_link=$(printf "%s" "$episode_link" | cut -d'>' -f2 | sed 's|repackager.wixmp.com/||g;s|\.urlset.*||g') + for j in $(printf "%s" "$episode_link" | sed -nE 's|.*/,([^/]*),/mp4.*|\1|p' | sed 's|,|\ +|g'); do + printf "%s >%s\n" "$j" "$extract_link" | sed "s|,[^/]*|${j}|g" + done | sort -nr + ;; + *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 "wixmp" "/Default :/p" ;; # wixmp(default)(m3u8)(multi) -> (mp4)(multi) + 2) provider_init "dropbox" "/Sak :/p" ;; # dropbox(mp4)(single) + 3) provider_init "wetransfer" "/Kir :/p" ;; # wetransfer(mp4)(single) + 4) 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 5" + 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" + 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}" +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 ;; + --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 <http://www.gnu.org/licenses/>. +# +# Project repository: https://github.com/pystardust/ani-cli </details> <p>After reinstalling and changing the cmd file, the update is working!!</p> <details> <summary>Update</summary> PowerShell 7.4.2 PS C:\Users\dhruv> scoop uninstall ani-cli Uninstalling 'ani-cli' (4.8). Removing shim 'ani-cli'. Removing shim 'ani-cli.cmd'. Unlinking ~\scoop\apps\ani-cli\current 'ani-cli' was uninstalled. PS C:\Users\dhruv> scoop install ani-cli Updating Scoop... Updating Buckets... * d24c5fdd3134 watchman: Update to version 2024.05.20.00 main 3 hours ago * ce04d5b83a03 oh-my-posh: Update to version 20.0.1 main 3 hours ago * 68409b235e8e red: Update to version 20may24 main 7 hours ago * 665b1f8c36e5 red-view: Update to version 20may24 main 7 hours ago * bf05086aa52c red-toolchain: Update to version 20may24 main 7 hours ago * 0e91b06a3730 openapi-generator-cli: Update to version 7.6.0 main 7 hours ago * 49144b962e41 doxygen: Update to version 1.11.0 main 7 hours ago * 47e6bc1d6696 mihomo@1.18.5: Fix hash (Closes #5854) main 8 hours ago * 97a63c388d5e wstunnel: Update to version 9.5.1 main 11 hours ago * a0351ff69f6a tup: Update to version 0.8-7 main 11 hours ago * 319398f28d10 trivy: Update to version 0.51.2 main 11 hours ago * 32436fb8a92d sqldef: Update to version 0.17.10 main 11 hours ago * 9f1af27ff80f slang: Update to version 2024.1.17 main 11 hours ago * 30133a40d110 rust-analyzer: Update to version 2024-05-20 main 11 hours ago * 278e9343d9d2 python: Tweak manifest (#5850) main 13 hours ago * d0add1e4d8ac autocorrect: Update to version 2.9.3 main 15 hours ago * 7ab10179c9a6 unrar@7.0.1: Fix hash (#5839) main 17 hours ago * 36288e1e47ee chore: Replace deprecated `env` function with `(Set|Ge.. main 17 hours ago * 5f47f94129ea tesseract: Fix extraction (#5846) main 18 hours ago * 9e46b536534a static-web-server: Update to version 2.31.0 main 19 hours ago * 22fd315649ce realm: Update to version 2.6.0 main 19 hours ago * 8061e92d6509 mingw-mstorsjo-llvm-ucrt: Update to version 18.1.6-202.. main 19 hours ago * 7038ce57699f flaresolverr: Update to version 3.3.18 main 19 hours ago * 7d302aa68c11 win-acme: Update to version 2.2.8.1635 main 23 hours ago * 7577e64c1491 golangci-lint: Update to version 1.58.2 main 23 hours ago * ee1aa02f1524 sqlpage: Update to version 0.21.0 main 27 hours ago * 1e22c0bdee99 sing-box: Update to version 1.8.14 main 27 hours ago * 9a8f43b751a4 mediamtx: Update to version 1.8.2 main 27 hours ago * 4a1c2f804422 wstunnel: Update to version 9.5.0 main 31 hours ago * 9a1be8996d4d tesseract: Update to version 5.3.4.20240503 main 31 hours ago * 3f493355005d sqldef: Update to version 0.17.9 main 31 hours ago * 636e6d47bdc3 oh-my-posh: Update to version 20.0.0 main 31 hours ago * db05a3e5a20d luau-lsp: Update to version 1.29.1 main 31 hours ago * 6d1a8ee584bb whisper-cpp: Update to version 1.6.0 main 35 hours ago * a67fc913b32a redis: Update to version 7.2.5 main 35 hours ago * 2c8db14e6cbc oh-my-posh: Update to version 19.32.0 main 35 hours ago * 877ffb9d8237 gitql: Update to version 0.19.1 main 35 hours ago * 8ee8854a657e dbmate: Update to version 2.16.0 main 35 hours ago * 9db096f80903 red: Update to version 19may24 main 2 days ago * 53fd9a651ad0 red-view: Update to version 19may24 main 2 days ago * 651a4944590d red-toolchain: Update to version 19may24 main 2 days ago * be315069ddca mihomo@1.18.5: Fix hash (Closes #5845) main 2 days ago * 36ff212114d8 racket: Update to version 8.13 main 2 days ago * 901a9e40712b racket-minimal: Update to version 8.13 main 2 days ago * 3da535ecab37 pandoc-crossref: Update to version 0.3.17.1a main 2 days ago * c9873027777e aqua: Update to version 2.28.1 main 2 days ago * 9fbde7710587 terragrunt: Update to version 0.58.7 main 2 days ago * c453035109f8 vfox: Update to version 0.5.3 main 2 days ago * 84b0bd64b996 importexcel: Update to version 7.8.9 main 2 days ago * 1ae15e950f38 win-vind: Update to version 5.13.0 extras 3 hours ago * 2e32b4f8b5eb sidekick-browser: Update to version 122.59.1.38681-203.. extras 3 hours ago * 53e28477bf3d crunchy-cli: Update to version 3.6.3 extras 3 hours ago * 85ac3be63952 castle-view-image: Update to version 2.2.0 extras 3 hours ago * 72c209e87c85 zoom: Update to version 6.0.10.39647 extras 7 hours ago * 729c1a5931c3 xmedia-recode: Update to version 3.5.9.6 extras 7 hours ago * 5618603320ff sidekick-browser: Update to version 120.58.1.38488-aa4.. extras 7 hours ago * 0946e87940d9 s3browser: Update to version 11.7.5 extras 7 hours ago * abcc9ff80dba resizer: Update to version 2.2 extras 7 hours ago * c5fc7431eb2c microsip: Update to version 3.21.4 extras 7 hours ago * 5c376931d923 kdiff3: Update to version 1.11.1 extras 7 hours ago * 2d72dd2f81fc database.net: Update to version 36.0.8906.2 extras 7 hours ago * bbd062c123c8 cudatext: Update to version 1.214.6.4 extras 7 hours ago * f04e9444e02b sidekick-browser: Update to version 122.60.1.39616-113.. extras 11 hours ago * dbf1863f32d8 responsively: Update to version 1.11.1 extras 11 hours ago * 82db541422da jackett: Update to version 0.21.2644 extras 11 hours ago * 2d902c34015e glary-utilities: Update to version 6.10 extras 11 hours ago * 161c84ac14f5 sidekick-browser: Update to version 120.58.1.38488-aa4.. extras 15 hours ago * fa6e24a8f940 gui-for-clash: Update to version 1.7.2 extras 15 hours ago * 1bb57985dc16 qtemu@2.1: Fix hash (Closes #13316) extras 17 hours ago * 18f9932db3e7 windowtextextractor: Update to version 2.1.2 extras 19 hours ago * 4e3330168d2d stirling-pdf: Update to version 0.24.3 extras 19 hours ago * 8cbfd090d6f1 sidekick-browser: Update to version 122.60.1.39616-113.. extras 19 hours ago * 8217fd4af365 scrawler: Update to version 2024.5.19.0 extras 23 hours ago * 47bb35baa5b8 openark: Update to version 1.3.6 extras 23 hours ago * 605e72f748ac ventoy: Update to version 1.0.98 extras 27 hours ago * 2dc8db53a401 synfig-studio: Update to version 1.4.5 extras 27 hours ago * ae9fd64bc4f4 stirling-pdf: Update to version 0.24.2 extras 27 hours ago * 6ed30fbfe6ec oculante: Update to version 0.8.22 extras 27 hours ago * 7603a381cff4 hibit-uninstaller: Update to version 3.2.20 extras 27 hours ago * e7da581964a2 helm-ls: Update to version 0.0.17 extras 27 hours ago * 884c8140b5fb electerm: Update to version 1.39.18 extras 27 hours ago * a312d7c4c556 dbeaver: Update to version 24.0.5 extras 27 hours ago * c966702fb70e stirling-pdf: Update to version 0.24.1 extras 31 hours ago * a074065e7881 smartsystemmenu: Update to version 2.26.1 extras 31 hours ago * 40ef554ecdaa lazygit: Update to version 0.42.0 extras 31 hours ago * 2ff9f5f8e2a7 gui-for-clash: Update to version 1.7.1 extras 31 hours ago * 405d806bbee4 firemin@11.8.3.8398: Fix hash (Closes #13314) extras 34 hours ago * f497ea291d9f virtualhere-client: Update to version 5.6.4 extras 35 hours ago * 1866a9dbc4ad notepad2-zufuliu: Update to version 4.24.05r5242 extras 35 hours ago * 6355238debd9 magpie: Update to version 0.11.0 extras 35 hours ago * 1e7e361f3011 jackett: Update to version 0.21.2635 extras 35 hours ago * d3bdc7f526bf cudatext: Update to version 1.214.6.3 extras 35 hours ago * 4898b890d590 sidekick-browser: Update to version 120.58.1.38488-aa4.. extras 2 days ago * e038b06a0bdd infoqube: Update to version 125.6 extras 2 days ago * cb717e9bbd8c celestia@1.6.4: Fix homepage url extras 2 days ago * 0707d4dd1b13 mpv-git: Update to version 20240519 extras 2 days ago * 491302849a64 horizon-eda: Update to version 2.6.0 extras 2 days ago * 0de75c05f6a8 sidekick-browser: Update to version 122.59.1.38681-203.. extras 2 days ago * a4447a9a33dc librewolf: Update to version 126.0-1 extras 2 days ago * 80c26eec0c78 floorp: Update to version 11.13.1 extras 2 days ago * 3195e0ebb5f9 cider: Update to version 1.6.3 extras 2 days ago * d266624fea52 xpipe: Update to version 9.3 extras 2 days ago * a6f8c39ef080 ungoogled-chromium: Update to version 125.0.6422.60-1.1 extras 2 days ago * f21cd85fc9ee sidekick-browser: Update to version 120.58.1.38488-aa4.. extras 2 days ago * 0411a1f0a08d saber: Update to version 0.23.2 extras 2 days ago * 75898971c8c8 floorp: Update to version 11.13.0 extras 2 days ago * a74f161af28c fancontrol: Update to version 191 extras 2 days ago Scoop was updated successfully! WARN Scoop uses 'aria2c' for multi-connection downloads. WARN Should it cause issues, run 'scoop config aria2-enabled false' to disable it. WARN To disable this warning, run 'scoop config aria2-warning-enabled false'. Installing 'ani-cli' (4.8) [64bit] from 'extras' bucket Loading ani-cli from cache. Checking hash of ani-cli ... ok. Linking ~\scoop\apps\ani-cli\current => ~\scoop\apps\ani-cli\4.8 Creating shim for 'ani-cli'. 'ani-cli' (4.8) was installed successfully! PS C:\Users\dhruv> ani-cli -U patching file 'C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli' Script has been updated PS C:\Users\dhruv> </details> <p>still same problem with bash even after updating: <img src="https://github.com/pystardust/ani-cli/assets/120670739/59e7316a-b944-44b9-af84-c971f1396222" alt="image" /></p> <p>again as long as its working in powershell, i am happy but if you want to debug the git bash for future references lemme know</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/ykhan21"><img src="https://avatars.githubusercontent.com/u/51864191?v=4" />ykhan21</a> commented <strong> 1 month ago</strong> </div> <div class="markdown-body"> <blockquote> <p>... PS C:\Users\dhruv> ani-cli -U patching file 'C:\Users\dhruv\scoop\apps\ani-cli\current\ani-cli' Script has been updated ...</p> </blockquote> <p>Your <code>ani-cli -U</code> works now, right? Great.</p> <p>Just remember to <code>ani-cli -U</code> to update the script every once in a while. Don't use <code>scoop update ani-cli</code> to update it unless the scoop install script is fixed.</p> <p>As for the Git Bash problem, could you raise a new issue? We can try to fix it there. Someone else had this issue but it was unresolved <a href="https://github.com/pystardust/ani-cli/issues/1097">https://github.com/pystardust/ani-cli/issues/1097</a>.</p> </div> </div> <div class="page-bar-simple"> </div> <div class="footer"> <ul class="body"> <li>© <script> document.write(new Date().getFullYear()) </script> Githubissues.</li> <li>Githubissues is a development platform for aggregating issues.</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script> <script src="/githubissues/assets/js.js"></script> <script src="/githubissues/assets/markdown.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/languages/go.min.js"></script> <script> hljs.highlightAll(); </script> </body> </html>