From 4674574c57b2ae567fc0dd3314ef877eaf3c6a58 Mon Sep 17 00:00:00 2001 From: coolnsx Date: Sat, 24 Dec 2022 20:16:44 +0530 Subject: [PATCH 01/40] resourced to allanime Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 698 ++++---------------------------------------------------- 1 file changed, 50 insertions(+), 648 deletions(-) mode change 100644 => 100755 ani-cli diff --git a/ani-cli b/ani-cli old mode 100644 new mode 100755 index 1716e89b4..91af08fc0 --- a/ani-cli +++ b/ani-cli @@ -1,671 +1,73 @@ #!/bin/sh -#--- -#dependencies: -# curl.se: ^7.8 -# openssl.org: ^1.1 -# ffmpeg.org: ^5.1 -#--- - -# License preamble at the end of the file -# Version number -VERSION="3.4.7" - - -####################### -# AUXILIARY FUNCTIONS # -####################### - -help_text () { - while read -r line; do - printf "%s\n" "$line" - done <<-EOF - - Usage: - ${0##*/} [-f] [-s] [-v] [-x] [-q ] [-a ] [-d | -p ] [] [-r ] - ${0##*/} [-f] [-s] [-v] [-x] [-q ] -c - ${0##*/} -h | -D | -U | -V - - Options: - -v use VLC as the media player - -q set video quality (best|worst|360|480|720|1080) - -s watch anime together with friends, using Syncplay (works with mpv only) - -f use fzf for anime selection - -a specify episode to watch - -d download episode - -p download episode to specified directory - -c continue watching anime from history - -h show helptext - -D delete history - -U fetch update from github - -V print version number and exit - -r select provider to scrape first [1-3] - -x print all video links from all providers to stdout (for debugging purpose) - - Episode selection: - Multiple episodes can be chosen given a range - Choose episode [1-13]: 1 6 - This would choose episodes 1 2 3 4 5 6 - To select the last episode use -1 - - When selecting non-interactively, the first result will be - selected, if anime is passed -EOF -} - -retry () { - err "$*" - prompt -} - -# display an error message to stderr (in red) -err () { - printf "\33[2K\r\033[1;31m%s\033[0m\n" "$*" >&2 -} - -#display error message and exit -die () { - err "$*" - exit 1 -} - -# display an informational message (first argument in green, second in magenta) -inf () { - printf "\33[2K\r\033[1;35m%s \033[1;35m%s\033[0m\n" "$1" "$2" -} - -progress() { - printf "\33[2K\r\033[1;34m%s\033[0m" "$1" -} - -debug () { - printf "\n\033[1;32mReferrer :\033[0m %s\n\033[1;32mlinks >>\n\033[0m%s\n" "$1" "$2" -} - -# prompts the user with message in $1-2 ($1 in blue, $2 in magenta) and saves the input to the variables in $REPLY and $REPLY2 -prompt () { - [ -n "$*" ] && printf "\33[2K\r\033[1;35m%s\n" "$*" - printf "\033[1;35m> \033[0m" - read -r REPLY REPLY2 -} - -selection_menu() { - menu_line_parity=0 - while read -r option line; do - if [ "$option" = "q" ]; then - printf "\033[1;31m(\033[1;31m%s\033[1;31m) \033[1;31m%s\033[0m\n" "$option" "$line" - else - if [ "$menu_line_parity" -eq 0 ]; then - printf "\033[1;33m(\033[1;33m%s\033[1;33m) \033[1;33m%s\033[0m\n" "$option" "$line" - menu_line_parity=1 - else - printf "\033[1;36m(\033[1;36m%s\033[1;36m) \033[1;36m%s\033[0m\n" "$option" "$line" - menu_line_parity=0 - fi - fi - done <<-EOF - $* - EOF - prompt -} - -selection_menu_fzf() { - printf "%s\n%s" "$1" "$2" | fzf --height=30% --border -1 --layout=reverse --header-first --header-lines=1 --cycle --with-nth 2.. | cut -d\ -f1 -} - -version_text () { - inf "Version: $VERSION" -} - -# get the newest version of this script from github and replace it -update_script () { - update="$(curl -A "$agent" -s "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 - inf "Script is up to date :)" - else - if printf '%s\n' "$update" | patch "$0" - ; then - inf "Script has been updated" - else - die "Can't update for some reason!" - fi - fi - exit 0 -} - -# checks if dependencies are present -dep_ch () { - for dep; do - if ! command -v "$dep" >/dev/null ; then - die "Program \"$dep\" not found. Please install it." - fi - done -} - -download () { - case $2 in - *m3u8*) - ffmpeg -loglevel error -stats -referer "$1" -i "$2" -c copy "$download_dir/$3.mp4" ;; - *) - axel -a -k -n 10 --header=Referer:"$1" "$2" -o "$download_dir/$3.mp4" ;; - esac -} - -############# -# SEARCHING # -############# - -# gets anime names along with its id for search term -search_anime () { - search=$(printf '%s' "$1" | tr ' ' '-' ) - curl -s "https://gogoanime.dk//search.html?keyword=$search" -L | - sed -nE "s_^[[:space:]]*\$_\1_p" +#search the query and give results +search_anime(){ + query=$(printf "%s" "$*" | sed 's/ /%20/g') + curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22allowUnknown%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's/Show/\n/g' | sed -nE "s/.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*/\1\t\2(\3 episode)/p" } -#fetches all the episodes embed links in an anime from gogoanime server -episode_list () { - select_ep_result=$(curl -A "$agent" -s "$base_url/v1/$1" | sed -nE "s_.*epslistplace.*>(.*)_\1_p" | tr "," "\n" | sed -e '/extra/d' -e '/PV/d' | sed -nE 's_".*":"(.*)".*_\1_p') - first_ep_number=1 - [ -z "$select_ep_result" ] && last_ep_number=0 || last_ep_number=$(printf "%s\n" "$select_ep_result" | wc -l) +#get the episodes list of the selected anime +episodes_list(){ + curl -s "https://allanime.site/anime/$*" -A "$agent" | sed 's/\\//g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's/,/\n/g;s/"//g' } -process_hist_entry () { - temp_anime_id=$(printf "%s" "$anime_id" | sed 's/\t[0-9]*.$//') - current_ep=$(printf "%s" "$anime_id" | sed "s/$temp_anime_id\t//g") - episode_list "$temp_anime_id" - latest_ep=$last_ep_number - if [ -n "$latest_ep" ] && [ "$latest_ep" -ge "$current_ep" ]; then - printf "%s : %s / %s\n" "$temp_anime_id" "$current_ep" "$latest_ep" - fi +#get the embed urls of the selected episode +embed_urls(){ + curl -s "https://allanime.site/watch/$id/$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's/\\u002F/\//g;s/\\//g' | sed -nE 's_.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*_\2 :\1_p' } -# compares history with gogoplay, only shows unfinished anime -search_history () { - [ ! -s "$logfile" ] && die "History is empty" - progress "Processing $scrape.." - search_results=$(while read -r anime_id; do process_hist_entry & done < "$logfile"; wait) - [ -z "$search_results" ] && die "No unwatched episodes" - one_hist=$(printf '%s\n' "$search_results" | grep -e "$" -c) - [ "$one_hist" = 1 ] && select_first=1 - anime_selection "$search_results" - ep_choice_start=$(sed -n -E "s/${selection_id}\t(.*)/\1/p" "$logfile") +#extract the video links from reponse of embed urls +get_links(){ + curl -s "$(printf "%s" "https://blog.allanime.pro/apivtwo/clock.json?id=$*" | sed 's/\\u002F/\//g')" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p' } -################## -# URL PROCESSING # -################## - -generate_link() { +#generates links based on given provider +generate_link(){ case $1 in - 2) - provider_name='Xstreamcdn' - progress "Fetching $provider_name links.." - fb_id=$(printf "%s" "$resp" | sed -n "s_.*fembed.*/v/__p") - refr="https://fembed-hd.com/v/$fb_id" - [ -z "$fb_id" ] && return 0 - result_links="$(curl -A "$agent" -s -X POST "https://fembed-hd.com/api/source/$fb_id" -H "x-requested-with:XMLHttpRequest" | - sed -e 's/\\//g' -e 's/.*data"://' | tr "}" "\n" | sed -nE 's/.*file":"(.*)","label":"(.*)","type.*/\2>\1/p')" - ;; 1) - provider_name='Animixplay' - progress "Fetching $provider_name Direct link.." - refr="$base_url" - [ -z "$id" ] && return 0 - enc_id=$(printf "%s" "$id" | base64) - ani_id=$(printf "%sLTXs3GrU8we9O%s" "$id" "$enc_id" | base64) - result_links="$(curl -s "$base_url/api/cW9${ani_id}" -A "$agent" -I | sed -nE 's_[L|l]ocation: https?://[^#]*#([^#]*).*_\1_p' | base64 -d)" - ;; - *) - provider_name='Gogoanime' - progress "Fetching $provider_name Direct link.." - refr="$gogohd_url" - [ -z "$id" ] && return 0 - secret_key=$(printf "%s" "$resp" | sed -n '2p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n") - iv=$(printf "%s" "$resp" | sed -n '3p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n") - second_key=$(printf "%s" "$resp" | sed -n '4p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n") - token=$(printf "%s" "$resp" | head -1 | base64 -d | openssl enc -d -aes256 -K "$secret_key" -iv "$iv" | sed -nE 's/.*&(token.*)/\1/p') - ajax=$(printf '%s' "$id" | openssl enc -e -aes256 -K "$secret_key" -iv "$iv" -a) - data=$(curl -A "$agent" -sL -H "X-Requested-With:XMLHttpRequest" "${gogohd_url}encrypt-ajax.php?id=${ajax}&alias=${id}&${token}" | sed -e 's/{"data":"//' -e 's/"}/\n/' -e 's/\\//g') - result_links="$(printf '%s' "$data" | base64 -d 2>/dev/null | openssl enc -d -aes256 -K "$second_key" -iv "$iv" 2>/dev/null | - tr -d \\\\ | sed -nE "s_.*file\":\"([^\"]*)\".*source.*_\1_p")" - ;; - esac -} - -# chooses the link for the set quality -get_video_link() { - dpage_url="$1" - id=$(printf "%s" "$dpage_url" | sed -nE 's/.*id=([^&]*).*/\1/p') - #multiple sed are used (regex seperated by ';') for extracting only required data from response of embed url - resp="$(curl -A "$agent" -sL "${gogohd_url}streaming.php?id=$id" | - sed -nE 's/.*class="container-(.*)">/\1/p ; - s/.*class="wrapper container-(.*)">/\1/p ; - s/.*class=".*videocontent-(.*)">/\1/p ; - s/.*data-value="(.*)">.*/\1/p ; - s/.*data-status="1".*data-video="(.*)">.*/\1/p')" - provider=1 - [ -n "$select_provider" ] && provider="$select_provider" - i=0 - while [ "$i" -lt 3 ] && [ -z "$result_links" ];do - generate_link "$provider" - provider=$((provider % 3 + 1)) - if [ "$debug" -eq 1 ]; then - debug "$refr" "$result_links" - unset result_links - fi - : $((i+=1)) - done - [ "$debug" -eq 1 ] && return 0 - if printf '%s' "$result_links" | grep -q "m3u8"; then - get_video_quality_m3u8 "$result_links" - else - video_url=$(get_video_quality_mp4 "$result_links") - fi - unset result_links -} - -get_video_quality_mp4() { - case $quality in - best) - video_url=$(printf '%s' "$1" | tail -n 1 | cut -d">" -f2) ;; - worst) - video_url=$(printf '%s' "$1" | head -n 1 | cut -d">" -f2) ;; - *) - video_url=$(printf '%s' "$1" | grep -i "${quality}p" | head -n 1 | cut -d">" -f2) - if [ -z "$video_url" ]; then - err "Current video quality is not available (defaulting to best quality)" - video_url=$(printf '%s' "$1" | tail -n 1 | cut -d">" -f2) - fi + #vrv,wixmp(default)(m3u8)(multi) + provider_name='vrv|wixmp' + provider_id=$(printf "%s" "$resp" | sed -n '/Default :/p' | head -1 | cut -d':' -f2) ;; - esac - printf '%s' "$video_url" -} - -get_video_quality_m3u8() { - printf '%s' "$1" | grep -qE "manifest.*m3u.*" && video_url=$1 && return 0 - m3u8_links=$(curl -A "$agent" -s --referer "$dpage_link" "$1") - case $quality in - best) - res_selector=$(printf "%s" "$m3u8_links" | sed -nE 's_.*RESOLUTION=.*x([^,]*).*_\1_p' | sort -nr | head -1);; - worst) - res_selector=$(printf "%s" "$m3u8_links" | sed -nE 's_.*RESOLUTION=.*x([^,]*).*_\1_p' | sort -nr | tail -1);; - *) - res_selector=$quality - if ! (printf '%s' "$m3u8_links" | grep -q "x$quality"); then - err "Current video quality is not available (defaulting to best quality)" - res_selector=$(printf "%s" "$m3u8_links" | sed -nE 's_.*RESOLUTION=.*x([^,]*).*_\1_p' | sort -nr | head -1) - fi + 2) + #pstatic(default backup)(mp4)(multi) + provider_name='pstatic' + provider_id=$(printf "%s" "$resp" | sed -n '/Default B :/p' | head -1 | cut -d':' -f2) ;; - esac - video_url=$(printf '%s' "$m3u8_links" | sed -n "/x$res_selector/{n;p;}" | tr -d '\r') - printf "%s" "$m3u8_links" | grep -q "http" || video_url="$(printf "%s" "$1" | sed 's|[^/]*$||')$video_url" || true -} - - -################# -# INPUT PARSING # -################# - -append () { - [ -z "$1" ] || printf "%s\n" "$1" - printf "%s %s" "$3" "$2" -} - -# only lets the user pass in case of a valid search -process_search () { - progress "Searching $scrape.." - search_results=$(search_anime "$query") - while [ -z "$search_results" ]; do - retry 'No search results found' - query="$REPLY $REPLY2" - progress "Searching $scrape.." - search_results=$(search_anime "$query") - done - anime_selection "$search_results" - episode_selection -} - -# anime-selection menu handling function -anime_selection () { - if [ "$fzf" -eq 0 ];then - inf "$scrape Results >>" - else - progress "" - fi - count=1 - unset selection_list - while read -r anime_id; do - displayed_title=$(printf '%s' "$anime_id" | tr '-' ' ') - selection_list=$(append "$selection_list" "$displayed_title" "$count") - : $((count+=1)) - done <<-EOF - $search_results - EOF - if [ -n "$select_first" ]; then - tput clear - choice=1 - elif [ -z "$ep_choice_to_start" ] || { [ -n "$ep_choice_to_start" ] && [ -z "$select_first" ]; }; then - selection_list=$(append "$selection_list" "quit" "q") - if [ "$fzf" -eq 1 ]; then - choice=$(selection_menu_fzf ". $scrape Results>>" "$selection_list") - [ -z "$choice" ] && exit 0 - else - selection_menu "$selection_list" - choice="$REPLY" - [ -z "$choice" ] && choice=1 - fi - while ! [ "$choice" -eq "$choice" ] 2>/dev/null || [ "$choice" -lt 1 ] || [ "$choice" -ge "$count" ] || [ "$choice" = " " ]; do - [ "$choice" = "q" ] && exit 0 - retry "Invalid choice entered" - choice="$REPLY" - done - fi - # Select respective anime_id - selection_id="$(printf "%s" "$search_results" | sed -n "${choice}p" | cut -d':' -f1 | tr -d ' ')" - progress "Searching Episodes.." - episode_list "$selection_id" -} - -# gets episode number from user, makes sure it's in range, skips input if only one episode exists -episode_selection () { - [ "$last_ep_number" -eq 0 ] && die "Episodes not released yet!" - if [ "$last_ep_number" -gt "$first_ep_number" ]; then - [ "$ep_choice_to_start" = "-1" ] && ep_choice_to_start="$last_ep_number" - if [ -z "$ep_choice_to_start" ]; then - # if branches, because order matters this time - while : ; do - last_ep_number=$(printf '%s' "$last_ep_number"|tr -d "[:space:]") - prompt "To specify a range, use: start_number end_number (Episodes: $first_ep_number-$last_ep_number)" - ep_choice_start="$REPLY" - ep_choice_end="$REPLY2" - [ "$REPLY" = q ] && exit 0 - [ "$ep_choice_start" = "-1" ] && ep_choice_start="$last_ep_number" || [ -z "$ep_choice_start" ] && ep_choice_start="$last_ep_number" - [ "$ep_choice_end" = "-1" ] && ep_choice_end="$last_ep_number" - if ! [ "$ep_choice_start" -eq "$ep_choice_start" ] 2>/dev/null || { [ -n "$ep_choice_end" ] && ! [ "$ep_choice_end" -eq "$ep_choice_end" ] 2>/dev/null; }; then - err "Invalid number(s)" - continue - fi - if [ "$ep_choice_start" -gt "$last_ep_number" ] 2>/dev/null || [ "$ep_choice_end" -gt "$last_ep_number" ] 2>/dev/null || [ "$ep_choice_start" -lt "$first_ep_number" ] 2>/dev/null; then - err "Episode out of range" - continue - fi - if [ -n "$ep_choice_end" ] && [ "$ep_choice_end" -le "$ep_choice_start" ]; then - err "Invalid range" - continue - fi - break - done - else - ep_choice_start="$ep_choice_to_start" && unset ep_choice_to_start - fi - else - # In case the anime contains only a single episode - ep_choice_start=1 - fi - [ -n "$ep_choice_end" ] && auto_play=1 -} - -# creates $episodes from $ep_choice_start and $ep_choice_end -generate_ep_list() { - episodes=$ep_choice_start - [ -n "$ep_choice_end" ] && episodes=$(seq "$ep_choice_start" "$ep_choice_end") -} - - -################## -# VIDEO PLAYBACK # -################## - -# opens selected episodes one-by-one -open_selection() { - for ep in $episodes; do - open_episode "$selection_id" "$ep" - done - episode=${ep_choice_end:-$ep_choice_start} -} - -open_episode () { - anime_id="$1" - episode="$2" - tput clear - progress "Loading episode $episode..." - # decrypting url - dpage_link=$(printf "%s" "$select_ep_result" | sed -n "${episode}p") - if [ -z "$dpage_link" ];then - die "Episode doesn't exist!!" - else - get_video_link "$dpage_link" - fi - [ "$debug" -eq 1 ] && exit 0 - # write anime and episode number and save to temporary history - grep -q -- "$selection_id" "$logfile" || printf "%s\t%s\n" "$selection_id" $((episode+1)) >> "$logfile" - sed -E "s/^${selection_id}\t[0-9]*/${selection_id}\t$((episode+1))/" "$logfile" > "${logfile}.new" - [ ! "$PID" = "0" ] && kill "$PID" >/dev/null 2>&1 - [ -z "$video_url" ] && die "Video URL not found" - trackma_title="$(printf '%s' "$anime_id Episode $episode" | tr '-' ' ' | awk '{for(i=1;i<=NF;i++){ $i=toupper(substr($i,1,1)) substr($i,2) }}1')" - if [ "$auto_play" -eq 0 ]; then - play_episode "$video_url" "$refr" "$trackma_title" - else - printf "\n" - play_episode "$video_url" "$refr" "$trackma_title" - wait - sleep 2 - fi - PID=$! - # overwrite history with temporary history - mv "${logfile}.new" "$logfile" -} - -play_episode () { - video_url="$1"; refr="$2" trackma_title="$3" - if [ "$player_fn" = "download" ];then - inf "Currently downloading $trackma_title ($provider_name)" - else - inf "Currently playing $trackma_title ($provider_name)" - fi - case "$player_fn" in - download) - if download "$refr" "$video_url" "$trackma_title"; then - inf "Downloaded episode: $trackma_title" - else - err "Download failed episode: $trackma_title , please retry or check your internet connection" - fi + 3) + #sharepoint(mp4)(single) + provider_name='sharepoint' + provider_id=$(printf "%s" "$resp" | sed -n '/S-mp4 :/p' | head -1 | cut -d':' -f2) ;; - iina) - nohup "$player_fn" "$video_url" --no-stdin --keep-running --mpv-referrer="$refr" --mpv-force-media-title="$trackma_title" > /dev/null 2>&1 & ;; - vlc) - if uname -a | grep -qE '[Aa]ndroid';then - am start --user 0 -a android.intent.action.VIEW -d "$video_url" -n org.videolan.vlc/org.videolan.vlc.gui.video.VideoPlayerActivity -e "title" "$trackma_title" > /dev/null 2>&1 & - else - nohup "$player_fn" "$video_url" --http-referrer="$refr" --meta-title "$trackma_title" --play-and-exit > /dev/null 2>&1 & - fi + 4) + #usercloud(mp4)(single) + provider_name='usercloud' + provider_id=$(printf "%s" "$resp" | sed -n '/Uv-mp4 :/p' | head -1 | cut -d':' -f2) ;; - "syncplay"|"/Applications/Syncplay.app/Contents/MacOS/syncplay"|"/c/Program Files (x86)/Syncplay/Syncplay.exe") - nohup "$player_fn" "$video_url" -- --referrer="$refr" --force-media-title="$trackma_title" > /dev/null 2>&1 & ;; *) - if uname -a | grep -qE '[Aa]ndroid';then - am start --user 0 -a android.intent.action.VIEW -d "$video_url" -n is.xyz.mpv/.MPVActivity > /dev/null 2>&1 & - else - nohup "$player_fn" "$video_url" --referrer="$refr" --force-media-title="$trackma_title" > /dev/null 2>&1 & - fi + #gogoanime(m3u8)(multi) + provider_name='gogoanime' + provider_id=$(printf "%s" "$resp" | sed -n '/Luf-mp4 :/p' | head -1 | cut -d':' -f2) ;; esac + #logic yet to implement + printf "\n\nFetching %s Links\n" "$provider_name" + [ -z "$provider_id" ] || get_links "$provider_id" } -############ -# START UP # -############ - -# clears the colors and deletes temporary logfile when exited using SIGINT -trap 'printf "\033[0m";rm -f "$logfile".new;exit 1' INT HUP - -# default options +#main +menu='fzf --layout=reverse --border --height=15' agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" -PID=0 -quality=best -scrape=Query -debug=0 -choice= -fzf=0 -auto_play=0 -download_dir="$(pwd)" -case "$(uname)" in - Darwin*) player_fn='iina';; - *) player_fn='mpv';; -esac -# history file path -logdir="${XDG_CACHE_HOME:-$HOME/.cache}" -logfile="$logdir/ani-hsts" -# create history file and history dir if none found -[ -d "$logdir" ] || mkdir "$logdir" -[ -f "$logfile" ] || : > "$logfile" - -while getopts 'svq:dp:chDUVa:xr:fn' OPT; do - case $OPT in - d) player_fn='download' ;; - a) ep_choice_to_start=$OPTARG ;; - U) update_script ;; - D) - : > "$logfile" - exit 0 - ;; - p) - player_fn='download' - download_dir="$OPTARG" - if [ ! -d "$download_dir" ] ; then - mkdir -p "$download_dir" || die "Couldn't create $download_dir" - fi - ;; - n) scrape=New ;; - s) - case "$(uname -s)" in - Darwin*) player_fn="/Applications/Syncplay.app/Contents/MacOS/syncplay" ;; - MINGW*|*Msys) player_fn="/c/Program Files (x86)/Syncplay/Syncplay.exe" ;; - *) player_fn="syncplay" ;; - esac - ;; - q) quality=$OPTARG ;; - x) debug=1 ;; - r) select_provider=$OPTARG ;; - f) fzf=1 ;; - c) scrape=History ;; - v) player_fn='vlc';; - V) - version_text - exit 0 - ;; - h) - help_text - exit 0 - ;; - *) - help_text - exit 1 - ;; - esac +mode="sub" +result=$(search_anime "$*" | $menu --with-nth=2..) +title=$(printf "%s" "$result" | cut -f2) +id=$(printf "%s" "$result" | cut -f1) +ep_no=$(episodes_list "$id" | $menu) +resp=$(embed_urls) +provider=1 +i=0 +while [ "$i" -lt 5 ];do + generate_link "$provider" + provider=$((provider % 5 + 1)) + : $((i+=1)) done -shift $((OPTIND - 1)) -progress "Checking dependencies.." -# shellcheck disable=SC2046 -dep_ch "curl" "sed" "grep" "openssl" || true - -if [ "$player_fn" = "download" ];then - dep_ch "ffmpeg" "axel" -elif ! (uname -a | grep -qE '[Aa]ndroid');then - dep_ch "$player_fn" -fi - -gogohd_url="https://gogohd.net/" -base_url="https://animixplay.to" -if [ "$scrape" = "Query" ];then - if [ -z "$*" ]; then - prompt "Search Anime" - query="$REPLY $REPLY2" - else - if [ -n "$ep_choice_to_start" ]; then - REPLY=1 - select_first=1 - fi - query="$*" - fi - process_search -elif [ "$scrape" = "New" ];then - progress "" - selection_id="$(curl -s "https://animixplay.to/rss.xml" | sed -nE 's_.*link.*animixplay.to/v1/([^<]*)/ep([^<]*)<.*_\1 episode \2_p' | tr '-' ' ' | fzf --height=30% --border -1 --layout=reverse --cycle)" - [ -z "$selection_id" ] && die "No anime Selected" - ep=$(printf "%s" "$selection_id" | sed 's_.*episode __') - selection_id=$(printf "%s" "$selection_id" | sed 's_ episode.*__' | tr ' ' '-') - episode_list "$selection_id" - open_episode "$selection_id" "$ep" - exit 0 -else - search_history -fi - -generate_ep_list -open_selection - -######## -# LOOP # -######## - -while : ; do - auto_play=0 - unset menu - unset options - [ "$episode" -ne "$last_ep_number" ] && menu=$(append "$menu" 'next' 'n') - [ "$episode" -ne "$first_ep_number" ] && menu=$(append "$menu" 'previous' 'p') - menu=$(append "$menu" 'replay' 'r') - [ "$first_ep_number" -ne "$last_ep_number" ] && menu=$(append "$menu" 'select' 's') - menu=$(append "$menu" 'quit' 'q') - if [ "$fzf" -eq 1 ];then - progress "" - choice="$(selection_menu_fzf ". Menu>>" "$menu")" - [ -z "$choice" ] && die "No anime Selected" - else - selection_menu "$menu" - choice="$REPLY" - fi - case $choice in - n|'') - ep_choice_start=$((episode + 1)) - unset ep_choice_end - ;; - p) - ep_choice_start=$((episode - 1)) - unset ep_choice_end - ;; - r) - ep_choice_start="$episode" - unset ep_choice_end - ;; - s) - episode_selection ;; - q) - break ;; - *) - tput clear - err "Invalid choice" - continue - ;; - esac - generate_ep_list - open_selection -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 . -# -# Project repository: https://github.com/pystardust/ani-cli From 1d6f425a89df8b301fad2aadcb65e29db0a8eed9 Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Sat, 24 Dec 2022 19:35:16 +0100 Subject: [PATCH 02/40] feat: nth and external launcher + refactor: removed no index from menus + refactor: replaced echo with printf + Done, to whoever merges this, good luck. + refactor: changed if to case statement + refactor: extracted `provider_init` as a function + chore: space between `#` and comments + chore(nth): shellcheck compliance, legibility + fix(nth): exit the program, not just the subshell + chore: sc2312 compliance + refactor: unite `get_links` and `m3u8 to mp4` + fix: removed rouge space from `get_links` + feat: searching works similarly how it did + chore: `search_anime` really has an argument now Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 134 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 47 deletions(-) diff --git a/ani-cli b/ani-cli index 91af08fc0..32469c1b5 100755 --- a/ani-cli +++ b/ani-cli @@ -1,73 +1,113 @@ #!/bin/sh -#search the query and give results -search_anime(){ - query=$(printf "%s" "$*" | sed 's/ /%20/g') - curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22allowUnknown%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's/Show/\n/g' | sed -nE "s/.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*/\1\t\2(\3 episode)/p" +use_external_menu=0 + +launcher() { + [ "$use_external_menu" = "0" ] && fzf --prompt "$1" + [ "$use_external_menu" = "1" ] && external_menu "$1" +} + +external_menu() { + rofi -dmenu -i -width 1500 -p "$1" +} + +nth() { + stdin=$(cat -) + [ -z "$stdin" ] && return 1 + line_count="$(printf "%s" "$stdin" | wc -l)" + [ "$line_count" -eq 1 ] && printf "%s" "$stdin" && return 0 + line=$(printf "%s" "$stdin" | sed 's/.*\t//' | launcher "") + [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" || exit 1 +} + +# search the query and give results +search_anime() { + curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22allowUnknown%22%3Afalse%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's/Show/\n/g' | sed -nE "s/.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*/\1\t\2 (\3 episode)/p" } -#get the episodes list of the selected anime -episodes_list(){ +# get the episodes list of the selected anime +episodes_list() { curl -s "https://allanime.site/anime/$*" -A "$agent" | sed 's/\\//g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's/,/\n/g;s/"//g' } -#get the embed urls of the selected episode -embed_urls(){ - curl -s "https://allanime.site/watch/$id/$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's/\\u002F/\//g;s/\\//g' | sed -nE 's_.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*_\2 :\1_p' +# get the embed urls of the selected episode +embed_urls() { + allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" + curl -s "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's/\\u002F/\//g;s/\\//g' | sed -nE 's_.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*_\2 :\1_p' } -#extract the video links from reponse of embed urls -get_links(){ - curl -s "$(printf "%s" "https://blog.allanime.pro/apivtwo/clock.json?id=$*" | sed 's/\\u002F/\//g')" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p' +# 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) } -#generates links based on given provider -generate_link(){ +# generates links based on given provider +generate_link() { case $1 in - 1) - #vrv,wixmp(default)(m3u8)(multi) - provider_name='vrv|wixmp' - provider_id=$(printf "%s" "$resp" | sed -n '/Default :/p' | head -1 | cut -d':' -f2) - ;; - 2) - #pstatic(default backup)(mp4)(multi) - provider_name='pstatic' - provider_id=$(printf "%s" "$resp" | sed -n '/Default B :/p' | head -1 | cut -d':' -f2) - ;; - 3) - #sharepoint(mp4)(single) - provider_name='sharepoint' - provider_id=$(printf "%s" "$resp" | sed -n '/S-mp4 :/p' | head -1 | cut -d':' -f2) - ;; - 4) - #usercloud(mp4)(single) - provider_name='usercloud' - provider_id=$(printf "%s" "$resp" | sed -n '/Uv-mp4 :/p' | head -1 | cut -d':' -f2) - ;; - *) - #gogoanime(m3u8)(multi) - provider_name='gogoanime' - provider_id=$(printf "%s" "$resp" | sed -n '/Luf-mp4 :/p' | head -1 | cut -d':' -f2) - ;; + 1) + # vrv,wixmp(default)(m3u8)(multi) + provider_init 'vrv|wixmp' '/Default :/p' ;; + 2) + # pstatic(default backup)(mp4)(multi) + provider_init 'pstatic' '/Default B :/p' ;; + 3) + # sharepoint(mp4)(single) + provider_init 'sharepoint' '/S-mp4 :/p' ;; + 4) + # usercloud(mp4)(single) + provider_init 'usercloud' '/Uv-mp4 :/p' ;; + *) + # gogoanime(m3u8)(multi) + provider_init 'gogoanime' '/Luf-mp4 :/p' ;; esac - #logic yet to implement + # logic yet to implement printf "\n\nFetching %s Links\n" "$provider_name" - [ -z "$provider_id" ] || get_links "$provider_id" + [ -n "$provider_id" ] && get_links "$provider_id" +} + +# extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists +get_links() { + series_link="$(printf "%s" "https://blog.allanime.pro/apivtwo/clock.json?id=$*" | sed 's/\\u002F/\//g')" + episode_link="$(curl -s "$series_link" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p')" + case "$episode_link" in + *v.vrv.co*) + extract_link=$(printf "%s" "$episode_link" | grep "$subtitle" | cut -d'>' -f2) + curl -s "$extract_link" | sed 's/^#.*x//g;s/,.*/p/g;/^#/d;$!N;s/\n/ >/;s/\/index-v1-a1\.m3u8//g' | sort -nr + ;; + *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/,/\n/g'); do + printf "%s >%s\n" "$j" "$extract_link" | sed "s_,[^/]*_${j}_g" + done | sort -nr + ;; + *) + printf "%s" "$episode_link" + ;; + esac } -#main -menu='fzf --layout=reverse --border --height=15' +# main + agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" -result=$(search_anime "$*" | $menu --with-nth=2..) +subtitle="en-US" +query="$*" +while [ -z "$query" ]; do + printf "Search anime\n>> " + read -r query +done +query=$(printf "%s" "$query" | sed "s/ /%20/g") +anime_list=$(search_anime "$query") +result=$(printf "%s" "$anime_list" | nth) || exit 1 title=$(printf "%s" "$result" | cut -f2) id=$(printf "%s" "$result" | cut -f1) -ep_no=$(episodes_list "$id" | $menu) +ep_no=$(episodes_list "$id" | nth) || exit 1 resp=$(embed_urls) provider=1 i=0 -while [ "$i" -lt 5 ];do +while [ "$i" -lt 5 ]; do generate_link "$provider" provider=$((provider % 5 + 1)) - : $((i+=1)) + : $((i += 1)) done From d7b132cb65d0d44345f3826265e147439844985d Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Mon, 26 Dec 2022 22:40:02 +0100 Subject: [PATCH 03/40] feat: loop control logic Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/ani-cli b/ani-cli index 32469c1b5..7d9510cab 100755 --- a/ani-cli +++ b/ani-cli @@ -87,12 +87,18 @@ get_links() { esac } +# todo +play_episode () { + printf "%s\n\r%s\n\r%s\n\r" "$title" "$id" "$ep_no" +} + # main agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" subtitle="en-US" query="$*" +debug_mode=1 while [ -z "$query" ]; do printf "Search anime\n>> " read -r query @@ -104,10 +110,39 @@ title=$(printf "%s" "$result" | cut -f2) id=$(printf "%s" "$result" | cut -f1) ep_no=$(episodes_list "$id" | nth) || exit 1 resp=$(embed_urls) -provider=1 -i=0 -while [ "$i" -lt 5 ]; do - generate_link "$provider" - provider=$((provider % 5 + 1)) - : $((i += 1)) -done +if [ "$debug_mode" -ne 0 ]; then + provider=1 + i=0 + while [ "$i" -lt 5 ]; do + generate_link "$provider" + provider=$((provider % 5 + 1)) + : $((i += 1)) + done +else + play_episode + while : ; do + cmd=$(printf "next\nreplay\nprevious\nselect\nquit" | nth) || exit 1 + case "$cmd" in + previous) # previous + ep_no="$((ep_no - 1))" + ;; + replay) + ;; + next) + ep_no="$((ep_no + 1))" + ;; + select) + printf "Select episode\n\r>> " + read -r ep_no + ;; + quit) + exit 0 + ;; + *) + printf "invalid input" + continue + ;; + esac + play_episode + done +fi From 8feebb2d42eb12521a49005ac92ad1f36cd0ed5a Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Mon, 26 Dec 2022 23:52:20 +0100 Subject: [PATCH 04/40] feat: `launcher` and `nth` prompt-message + feat: skeleton of `play_episode` controlled by `player_function` + fix: better previous, next and select options + refactor: `ep_list` variable for avoiding same web calls with `episodes_list` + chore: sc2249 compliance Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 97 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/ani-cli b/ani-cli index 7d9510cab..24c4c137a 100755 --- a/ani-cli +++ b/ani-cli @@ -3,12 +3,12 @@ use_external_menu=0 launcher() { - [ "$use_external_menu" = "0" ] && fzf --prompt "$1" - [ "$use_external_menu" = "1" ] && external_menu "$1" + [ "$use_external_menu" = "0" ] && fzf --prompt "$*" + [ "$use_external_menu" = "1" ] && external_menu "$*" } external_menu() { - rofi -dmenu -i -width 1500 -p "$1" + rofi -dmenu -i -width 1500 -p "$*" } nth() { @@ -16,7 +16,7 @@ nth() { [ -z "$stdin" ] && return 1 line_count="$(printf "%s" "$stdin" | wc -l)" [ "$line_count" -eq 1 ] && printf "%s" "$stdin" && return 0 - line=$(printf "%s" "$stdin" | sed 's/.*\t//' | launcher "") + line=$(printf "%s" "$stdin" | sed 's/.*\t//' | launcher "$*") [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" || exit 1 } @@ -88,61 +88,70 @@ get_links() { } # todo +# intent: player_function is set at program start, then the matching branch has the neccesary setup and the command. +# player_function can be a player program, download or debug. play_episode () { - printf "%s\n\r%s\n\r%s\n\r" "$title" "$id" "$ep_no" + case "$player_function" in + debug) + provider=1 + i=0 + while [ "$i" -lt 5 ]; do + generate_link "$provider" + provider=$((provider % 5 + 1)) + : $((i += 1)) + done + ;; + mpv|iina) + ;; + vlc) + ;; + syncplay) + ;; + download) + ;; + *) + printf "Invalid player function" + exit 1 + ;; + esac } # main - agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" subtitle="en-US" query="$*" -debug_mode=1 +player_function="debug" while [ -z "$query" ]; do printf "Search anime\n>> " read -r query done query=$(printf "%s" "$query" | sed "s/ /%20/g") anime_list=$(search_anime "$query") -result=$(printf "%s" "$anime_list" | nth) || exit 1 +result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 title=$(printf "%s" "$result" | cut -f2) id=$(printf "%s" "$result" | cut -f1) -ep_no=$(episodes_list "$id" | nth) || exit 1 +ep_list=$(episodes_list "$id") +ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 resp=$(embed_urls) -if [ "$debug_mode" -ne 0 ]; then - provider=1 - i=0 - while [ "$i" -lt 5 ]; do - generate_link "$provider" - provider=$((provider % 5 + 1)) - : $((i += 1)) - done -else + +play_episode +while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... ") ; do + case "$cmd" in + previous) + ep_no=$(printf "%s" "$ep_list" | grep -w -C 1 "$ep_no") 2>/dev/null || printf "Episode not available\n" && exit 0 + ;; + replay) + ;; + next) + ep_no=$(printf "%s" "$ep_list" | grep -w -C -1 "$ep_no") 2>/dev/null || printf "Episode not available\n" && exit 0 + ;; + select) + ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 + ;; + *) + exit 0 + ;; + esac play_episode - while : ; do - cmd=$(printf "next\nreplay\nprevious\nselect\nquit" | nth) || exit 1 - case "$cmd" in - previous) # previous - ep_no="$((ep_no - 1))" - ;; - replay) - ;; - next) - ep_no="$((ep_no + 1))" - ;; - select) - printf "Select episode\n\r>> " - read -r ep_no - ;; - quit) - exit 0 - ;; - *) - printf "invalid input" - continue - ;; - esac - play_episode - done -fi +done From 0b3fb945de5daa9bc8b235f7488d3a39353ec855 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Tue, 27 Dec 2022 00:11:28 +0100 Subject: [PATCH 05/40] chore: GPL3 preabmle + style: fix indentation + fix: fix changing episodes + refactor: add die function Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 108 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/ani-cli b/ani-cli index 24c4c137a..af0c8432e 100755 --- a/ani-cli +++ b/ani-cli @@ -20,6 +20,11 @@ nth() { [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" || exit 1 } +die() { + printf "%s\n" "$*" >&2 + exit 1 +} + # search the query and give results search_anime() { curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22allowUnknown%22%3Afalse%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's/Show/\n/g' | sed -nE "s/.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*/\1\t\2 (\3 episode)/p" @@ -47,19 +52,24 @@ generate_link() { case $1 in 1) # vrv,wixmp(default)(m3u8)(multi) - provider_init 'vrv|wixmp' '/Default :/p' ;; + provider_init 'vrv|wixmp' '/Default :/p' + ;; 2) # pstatic(default backup)(mp4)(multi) - provider_init 'pstatic' '/Default B :/p' ;; + provider_init 'pstatic' '/Default B :/p' + ;; 3) # sharepoint(mp4)(single) - provider_init 'sharepoint' '/S-mp4 :/p' ;; + provider_init 'sharepoint' '/S-mp4 :/p' + ;; 4) # usercloud(mp4)(single) - provider_init 'usercloud' '/Uv-mp4 :/p' ;; + provider_init 'usercloud' '/Uv-mp4 :/p' + ;; *) # gogoanime(m3u8)(multi) - provider_init 'gogoanime' '/Luf-mp4 :/p' ;; + provider_init 'gogoanime' '/Luf-mp4 :/p' + ;; esac # logic yet to implement printf "\n\nFetching %s Links\n" "$provider_name" @@ -69,7 +79,7 @@ generate_link() { # extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists get_links() { series_link="$(printf "%s" "https://blog.allanime.pro/apivtwo/clock.json?id=$*" | sed 's/\\u002F/\//g')" - episode_link="$(curl -s "$series_link" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p')" + episode_link="$(curl -s "$series_link" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p')" case "$episode_link" in *v.vrv.co*) extract_link=$(printf "%s" "$episode_link" | grep "$subtitle" | cut -d'>' -f2) @@ -90,29 +100,24 @@ get_links() { # todo # intent: player_function is set at program start, then the matching branch has the neccesary setup and the command. # player_function can be a player program, download or debug. -play_episode () { +play_episode() { case "$player_function" in - debug) - provider=1 - i=0 - while [ "$i" -lt 5 ]; do - generate_link "$provider" - provider=$((provider % 5 + 1)) - : $((i += 1)) - done - ;; - mpv|iina) - ;; - vlc) - ;; - syncplay) - ;; - download) - ;; - *) - printf "Invalid player function" - exit 1 - ;; + debug) + provider=1 + i=0 + while [ "$i" -lt 5 ]; do + generate_link "$provider" + provider=$((provider % 5 + 1)) + : $((i += 1)) + done + ;; + mpv | iina) ;; + vlc) ;; + syncplay) ;; + download) ;; + *) + die "Invalid player function" + ;; esac } @@ -136,22 +141,39 @@ ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 resp=$(embed_urls) play_episode -while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... ") ; do +while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in - previous) - ep_no=$(printf "%s" "$ep_list" | grep -w -C 1 "$ep_no") 2>/dev/null || printf "Episode not available\n" && exit 0 - ;; - replay) - ;; - next) - ep_no=$(printf "%s" "$ep_list" | grep -w -C -1 "$ep_no") 2>/dev/null || printf "Episode not available\n" && exit 0 - ;; - select) - ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 - ;; - *) - exit 0 - ;; + previous) + ep_no=$(printf "%s" "$ep_list" | grep -w "$((ep_no - 1))") 2>/dev/null || die "Episode not available" + ;; + replay) ;; + next) + ep_no=$(printf "%s\n" "$ep_list" | grep -w "$((ep_no + 1))") 2>/dev/null || die "Episode not available" + ;; + select) + ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 + ;; + *) + exit 0 + ;; esac + resp=$(embed_urls) play_episode 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 . +# +# Project repository: https://github.com/pystardust/ani-cli From 24b2b068fa76d212dfbe9a30747e5aa023203ef2 Mon Sep 17 00:00:00 2001 From: coolnsx Date: Tue, 27 Dec 2022 22:12:38 +0530 Subject: [PATCH 06/40] feat: gogoanime resolution parsing Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ani-cli b/ani-cli index af0c8432e..5b23ffa0e 100755 --- a/ani-cli +++ b/ani-cli @@ -14,7 +14,7 @@ external_menu() { nth() { stdin=$(cat -) [ -z "$stdin" ] && return 1 - line_count="$(printf "%s" "$stdin" | wc -l)" + line_count="$(printf "%s\n" "$stdin" | wc -l)" [ "$line_count" -eq 1 ] && printf "%s" "$stdin" && return 0 line=$(printf "%s" "$stdin" | sed 's/.*\t//' | launcher "$*") [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" || exit 1 @@ -27,7 +27,7 @@ die() { # search the query and give results search_anime() { - curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22allowUnknown%22%3Afalse%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's/Show/\n/g' | sed -nE "s/.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*/\1\t\2 (\3 episode)/p" + curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's/Show/\n/g' | sed -nE "s/.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*/\1\t\2 (\3 episode)/p" } # get the episodes list of the selected anime @@ -51,7 +51,7 @@ provider_init() { generate_link() { case $1 in 1) - # vrv,wixmp(default)(m3u8)(multi) + # vrv,wixmp(default)(m3u8)(multi) -> (mp4)(multi) provider_init 'vrv|wixmp' '/Default :/p' ;; 2) @@ -78,12 +78,11 @@ generate_link() { # extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists get_links() { - series_link="$(printf "%s" "https://blog.allanime.pro/apivtwo/clock.json?id=$*" | sed 's/\\u002F/\//g')" - episode_link="$(curl -s "$series_link" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p')" + episode_link="$(curl -s "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p')" case "$episode_link" in *v.vrv.co*) extract_link=$(printf "%s" "$episode_link" | grep "$subtitle" | cut -d'>' -f2) - curl -s "$extract_link" | sed 's/^#.*x//g;s/,.*/p/g;/^#/d;$!N;s/\n/ >/;s/\/index-v1-a1\.m3u8//g' | sort -nr + curl -s "$extract_link" -A "$agent" | sed 's/^#.*x//g;s/,.*/p/g;/^#/d;$!N;s/\n/ >/;s/\/index-v1-a1\.m3u8//g' | sort -nr ;; *repackager.wixmp.com*) extract_link=$(printf "%s" "$episode_link" | cut -d'>' -f2 | sed 's_repackager.wixmp.com/__g;s_\.urlset.*__g') @@ -91,6 +90,15 @@ get_links() { printf "%s >%s\n" "$j" "$extract_link" | sed "s_,[^/]*_${j}_g" done | sort -nr ;; + *//cache.*|*gofcdn.com*) + 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 -s "$extract_link" -A "$agent" | sed 's/^#.*x//g;s/,.*/p/g;/^#/d;$!N;s/\n/ >/' | sed "s|>|>${relative_link}|g" | sort -nr + fi + ;; *) printf "%s" "$episode_link" ;; From 61bdbb86f1f89b95efb32a4090e7ef7cf3662038 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Tue, 27 Dec 2022 18:18:31 +0100 Subject: [PATCH 07/40] chore: made all sed uniform with `|` separator *very much coauthored by 5 people \s* lol Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ani-cli b/ani-cli index 5b23ffa0e..08d4e545b 100755 --- a/ani-cli +++ b/ani-cli @@ -16,7 +16,7 @@ nth() { [ -z "$stdin" ] && return 1 line_count="$(printf "%s\n" "$stdin" | wc -l)" [ "$line_count" -eq 1 ] && printf "%s" "$stdin" && return 0 - line=$(printf "%s" "$stdin" | sed 's/.*\t//' | launcher "$*") + line=$(printf "%s" "$stdin" | sed 's|.*\t||' | launcher "$*") [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" || exit 1 } @@ -27,18 +27,18 @@ die() { # search the query and give results search_anime() { - curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's/Show/\n/g' | sed -nE "s/.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*/\1\t\2 (\3 episode)/p" + curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's|Show|\n|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*|\1\t\2 (\3 episode)|p" } # get the episodes list of the selected anime episodes_list() { - curl -s "https://allanime.site/anime/$*" -A "$agent" | sed 's/\\//g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's/,/\n/g;s/"//g' + curl -s "https://allanime.site/anime/$*" -A "$agent" | sed 's|\\||g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\n|g; s|"||g' } # get the embed urls of the selected episode embed_urls() { allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" - curl -s "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's/\\u002F/\//g;s/\\//g' | sed -nE 's_.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*_\2 :\1_p' + curl -s "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p' } # innitialises provider_name and provider_id. First argument is the provider name, 2nd is the regex that matches that provider's link @@ -78,16 +78,16 @@ generate_link() { # extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists get_links() { - episode_link="$(curl -s "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -A "$agent" | sed 's/},{/\n/g' | sed -nE 's_.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*_\2 >\1_p')" + episode_link="$(curl -s "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -A "$agent" | sed 's|},{|\n|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p')" case "$episode_link" in *v.vrv.co*) extract_link=$(printf "%s" "$episode_link" | grep "$subtitle" | cut -d'>' -f2) - curl -s "$extract_link" -A "$agent" | sed 's/^#.*x//g;s/,.*/p/g;/^#/d;$!N;s/\n/ >/;s/\/index-v1-a1\.m3u8//g' | sort -nr + curl -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr ;; *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/,/\n/g'); do - printf "%s >%s\n" "$j" "$extract_link" | sed "s_,[^/]*_${j}_g" + 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|,|\n|g'); do + printf "%s >%s\n" "$j" "$extract_link" | sed "s|,[^/]*|${j}|g" done | sort -nr ;; *//cache.*|*gofcdn.com*) @@ -96,7 +96,7 @@ get_links() { else extract_link=$(printf "%s" "$episode_link" | head -1 | cut -d'>' -f2) relative_link=$(printf "%s" "$extract_link" | sed 's|[^/]*$||') - curl -s "$extract_link" -A "$agent" | sed 's/^#.*x//g;s/,.*/p/g;/^#/d;$!N;s/\n/ >/' | sed "s|>|>${relative_link}|g" | sort -nr + curl -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|' | sed "s|>|>${relative_link}|g" | sort -nr fi ;; *) @@ -139,7 +139,7 @@ while [ -z "$query" ]; do printf "Search anime\n>> " read -r query done -query=$(printf "%s" "$query" | sed "s/ /%20/g") +query=$(printf "%s" "$query" | sed "s| |%20|g") anime_list=$(search_anime "$query") result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 title=$(printf "%s" "$result" | cut -f2) From 180206eec43567e4178cee040988af91071b2d2c Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Tue, 27 Dec 2022 19:54:42 +0100 Subject: [PATCH 08/40] fix: next/previous work for non-integer episodes + feat: async link extraction, `cache_dir`, `log_dir` Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/ani-cli b/ani-cli index 08d4e545b..633f8c998 100755 --- a/ani-cli +++ b/ani-cli @@ -72,7 +72,6 @@ generate_link() { ;; esac # logic yet to implement - printf "\n\nFetching %s Links\n" "$provider_name" [ -n "$provider_id" ] && get_links "$provider_id" } @@ -109,15 +108,23 @@ get_links() { # intent: player_function is set at program start, then the matching branch has the neccesary setup and the command. # player_function can be a player program, download or debug. play_episode() { + # clear cache + for file in "$cache_dir"/*; do + : > "$file" + done + # generate links into sequential files + provider=1 + i=0 + while [ "$i" -lt 5 ]; do + generate_link "$provider" > "$cache_dir"/"$i" & + provider=$((provider % 5 + 1)) + : $((i += 1)) + done + wait + #play the episode case "$player_function" in debug) - provider=1 - i=0 - while [ "$i" -lt 5 ]; do - generate_link "$provider" - provider=$((provider % 5 + 1)) - : $((i += 1)) - done + cat "$cache_dir"/* ;; mpv | iina) ;; vlc) ;; @@ -133,6 +140,12 @@ play_episode() { agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" subtitle="en-US" +cache_dir="${XDG_CACHE_HOME:-~/.cache}/ani-cli" +[ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" +log_dir="${XDG_STATE_HOME:-~/.local/state}/ani-cli" +[ ! -d "$log_dir" ] && mkdir -p "$log_dir" + + query="$*" player_function="debug" while [ -z "$query" ]; do @@ -145,18 +158,18 @@ result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 title=$(printf "%s" "$result" | cut -f2) id=$(printf "%s" "$result" | cut -f1) ep_list=$(episodes_list "$id") + ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 resp=$(embed_urls) - play_episode while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in previous) - ep_no=$(printf "%s" "$ep_list" | grep -w "$((ep_no - 1))") 2>/dev/null || die "Episode not available" + ep_no=$(printf "%s" "$ep_list" | grep -w -A 1 "$ep_no" | tail -n1) 2>/dev/null || die "Episode not available" ;; replay) ;; next) - ep_no=$(printf "%s\n" "$ep_list" | grep -w "$((ep_no + 1))") 2>/dev/null || die "Episode not available" + ep_no=$(printf "%s\n" "$ep_list" | grep -w -B 1 "$ep_no" | head -n1) 2>/dev/null || die "Episode not available" ;; select) ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 From aba867d2a605f399754ed48afd9d608c7df4aed1 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Tue, 27 Dec 2022 21:21:34 +0100 Subject: [PATCH 09/40] feat: quality selection works Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/ani-cli b/ani-cli index 633f8c998..853f0b5ac 100755 --- a/ani-cli +++ b/ani-cli @@ -104,6 +104,22 @@ get_links() { esac } +select_quality () { + case "$1" in + best) + result=$(cat "$cache_dir"/* | head -n1) + ;; + worst) + result=$(cat "$cache_dir"/* | tail -n1) + ;; + *) + result=$(cat "$cache_dir"/* | grep "$1") + ;; + esac + [ -z "$result" ] && printf "Specified quality not found, defaulting to best" && result=$(cat "$cache_dir"/* | head -n1) + printf "%s" "$result" | cut -d'>' -f2 +} + # todo # intent: player_function is set at program start, then the matching branch has the neccesary setup and the command. # player_function can be a player program, download or debug. @@ -115,16 +131,22 @@ play_episode() { # generate links into sequential files provider=1 i=0 - while [ "$i" -lt 5 ]; do + while [ "$i" -lt 5 ]; do generate_link "$provider" > "$cache_dir"/"$i" & + printf "Fetching %s Links\n" "$provider_name" provider=$((provider % 5 + 1)) : $((i += 1)) done wait + # select the link with matching quality + episode=$(select_quality "$quality") + [ -z "$episode" ] && die "Episode not released!" #play the episode case "$player_function" in debug) + printf "All links:\n" cat "$cache_dir"/* + printf "Selected link:\n%s\n" "$episode" ;; mpv | iina) ;; vlc) ;; @@ -140,6 +162,7 @@ play_episode() { agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" subtitle="en-US" +quality="best" cache_dir="${XDG_CACHE_HOME:-~/.cache}/ani-cli" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" log_dir="${XDG_STATE_HOME:-~/.local/state}/ani-cli" From bc60f4a59404201faa01e36aed2fdf9dfa2e9cae Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Fri, 30 Dec 2022 13:22:24 +0100 Subject: [PATCH 10/40] feat: leaner quality selection by @71zenith Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ani-cli b/ani-cli index 853f0b5ac..71eca0b9e 100755 --- a/ani-cli +++ b/ani-cli @@ -105,18 +105,19 @@ get_links() { } select_quality () { + links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g' | sort -g -r) case "$1" in best) - result=$(cat "$cache_dir"/* | head -n1) + result=$(printf "%s" "$links" | head -n1) ;; worst) - result=$(cat "$cache_dir"/* | tail -n1) + result=$(printf "%s" "$links" | grep -E '^[0-9]{3,4}' | tail -n1) ;; *) - result=$(cat "$cache_dir"/* | grep "$1") + result=$(printf "%s" "$links" | grep -m 1 "$1") ;; esac - [ -z "$result" ] && printf "Specified quality not found, defaulting to best" && result=$(cat "$cache_dir"/* | head -n1) + [ -z "$result" ] && printf "Specified quality not found, defaulting to best" 1>&2 && result=$(printf "%s" "$links" | head -n1) printf "%s" "$result" | cut -d'>' -f2 } From 00dc525c3d372a746682166ccd13feb10794faa7 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Tue, 27 Dec 2022 21:37:30 +0100 Subject: [PATCH 11/40] feat: mpv and untested vlc/syncplay/download + fix: Fetching links messages are printed to stderr + fix: quality errors are printed to stderr + fix: better episode increment/decrement by @CoolnsX + fix: really print to stderr Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ani-cli b/ani-cli index 71eca0b9e..fb617cd79 100755 --- a/ani-cli +++ b/ani-cli @@ -21,7 +21,7 @@ nth() { } die() { - printf "%s\n" "$*" >&2 + printf "%s\n" "$*" 1>&2 exit 1 } @@ -102,6 +102,7 @@ get_links() { printf "%s" "$episode_link" ;; esac + printf "Fetching %s Links\n" "$provider_name" 1>&2 } select_quality () { @@ -117,7 +118,7 @@ select_quality () { result=$(printf "%s" "$links" | grep -m 1 "$1") ;; esac - [ -z "$result" ] && printf "Specified quality not found, defaulting to best" 1>&2 && result=$(printf "%s" "$links" | head -n1) + [ -z "$result" ] && printf "Specified quality not found, defaulting to best" 1>&2 && result=$(cat "$cache_dir"/* | head -n1) printf "%s" "$result" | cut -d'>' -f2 } @@ -134,7 +135,6 @@ play_episode() { i=0 while [ "$i" -lt 5 ]; do generate_link "$provider" > "$cache_dir"/"$i" & - printf "Fetching %s Links\n" "$provider_name" provider=$((provider % 5 + 1)) : $((i += 1)) done @@ -149,10 +149,18 @@ play_episode() { cat "$cache_dir"/* printf "Selected link:\n%s\n" "$episode" ;; - mpv | iina) ;; - vlc) ;; - syncplay) ;; - download) ;; + mpv | iina) + "$player_function" "$episode" + ;; + vlc) + "$player_function" "$episode" + ;; + syncplay) + "$player_function" "$episode" + ;; + download) + aria2c "$episode" + ;; *) die "Invalid player function" ;; @@ -171,7 +179,7 @@ log_dir="${XDG_STATE_HOME:-~/.local/state}/ani-cli" query="$*" -player_function="debug" +player_function="mpv" while [ -z "$query" ]; do printf "Search anime\n>> " read -r query @@ -189,11 +197,11 @@ play_episode while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in previous) - ep_no=$(printf "%s" "$ep_list" | grep -w -A 1 "$ep_no" | tail -n1) 2>/dev/null || die "Episode not available" + ep_no=$(printf "%s" "$ep_list" | sed "\$\!N;/\n${ep_no}$/P;D") 2>/dev/null ;; replay) ;; next) - ep_no=$(printf "%s\n" "$ep_list" | grep -w -B 1 "$ep_no" | head -n1) 2>/dev/null || die "Episode not available" + ep_no=$(printf "%s\n" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null ;; select) ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 From fa84d9db7cdea3c03bb9988ced4f84877b5e2c36 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Tue, 27 Dec 2022 22:01:19 +0100 Subject: [PATCH 12/40] chore: removed shellcheck exceptions *magnificent commit* + chore: ignore SC2250 + fix: previous and next episode + fix: `$HOME` instead of `~` Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- .github/workflows/master.yml | 2 +- ani-cli | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 25b8e2642..a606fb4b2 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -13,4 +13,4 @@ jobs: - name: Run ShellCheck uses: ludeeus/action-shellcheck@master env: - SHELLCHECK_OPTS: -s sh -o all -e 2250 -e 2016 -x + SHELLCHECK_OPTS: -s sh -o all -e 2250 diff --git a/ani-cli b/ani-cli index fb617cd79..74188d58e 100755 --- a/ani-cli +++ b/ani-cli @@ -172,9 +172,9 @@ agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" subtitle="en-US" quality="best" -cache_dir="${XDG_CACHE_HOME:-~/.cache}/ani-cli" +cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" -log_dir="${XDG_STATE_HOME:-~/.local/state}/ani-cli" +log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli" [ ! -d "$log_dir" ] && mkdir -p "$log_dir" @@ -197,11 +197,11 @@ play_episode while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in previous) - ep_no=$(printf "%s" "$ep_list" | sed "\$\!N;/\n${ep_no}$/P;D") 2>/dev/null + ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null ;; replay) ;; next) - ep_no=$(printf "%s\n" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null + ep_no=$(printf "%s\n" "$ep_list" | sed -n "/^${ep_no}$/{g;1\!p;};h") 2>/dev/null ;; select) ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 From 7b95f6ff81df69a3ff5576d9682efa8b56a795a3 Mon Sep 17 00:00:00 2001 From: coolnsx Date: Wed, 28 Dec 2022 21:50:15 +0530 Subject: [PATCH 13/40] feat: downloads, fix : .5 episode streaming Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ani-cli b/ani-cli index 74188d58e..75eb94819 100755 --- a/ani-cli +++ b/ani-cli @@ -37,7 +37,6 @@ episodes_list() { # get the embed urls of the selected episode embed_urls() { - allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" curl -s "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p' } @@ -122,6 +121,15 @@ select_quality () { printf "%s" "$result" | cut -d'>' -f2 } +download () { + case $1 in + *m3u8*) + ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; + *) + aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; + esac +} + # todo # intent: player_function is set at program start, then the matching branch has the neccesary setup and the command. # player_function can be a player program, download or debug. @@ -159,7 +167,7 @@ play_episode() { "$player_function" "$episode" ;; download) - aria2c "$episode" + "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; *) die "Invalid player function" @@ -171,6 +179,7 @@ play_episode() { agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" subtitle="en-US" +download_dir="." quality="best" cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" @@ -188,6 +197,7 @@ query=$(printf "%s" "$query" | sed "s| |%20|g") anime_list=$(search_anime "$query") result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 title=$(printf "%s" "$result" | cut -f2) +allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" id=$(printf "%s" "$result" | cut -f1) ep_list=$(episodes_list "$id") @@ -201,7 +211,7 @@ while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode ;; replay) ;; next) - ep_no=$(printf "%s\n" "$ep_list" | sed -n "/^${ep_no}$/{g;1\!p;};h") 2>/dev/null + 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: ") || exit 1 @@ -210,6 +220,7 @@ while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode exit 0 ;; esac + [ -z "$ep_no" ] && die "Out of range" resp=$(embed_urls) play_episode done From 709b6dc25eac9cc9233847c9216e213a2eabd1f6 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Wed, 28 Dec 2022 18:21:13 +0100 Subject: [PATCH 14/40] feat: window titles Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ani-cli b/ani-cli index 75eb94819..af47d831b 100755 --- a/ani-cli +++ b/ani-cli @@ -158,13 +158,13 @@ play_episode() { printf "Selected link:\n%s\n" "$episode" ;; mpv | iina) - "$player_function" "$episode" + nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null & ;; vlc) - "$player_function" "$episode" + nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null & ;; syncplay) - "$player_function" "$episode" + nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" > /dev/null & ;; download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" From c846278697d3368f7db80922511eaf16ea0e5d6e Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Wed, 28 Dec 2022 18:33:54 +0100 Subject: [PATCH 15/40] fix: select quality returns multiple urls + fix: missing newline at the end of provider files + fix: best and worst quality now work 100% + chore: cleanup + refactor: moved link extraction from `play_episode` + refarctor: get_episode_url to handle url extraction from start to finish + refactor: moved functions into logical groups and hierarchical order Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 155 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 72 deletions(-) diff --git a/ani-cli b/ani-cli index af47d831b..dedca7827 100755 --- a/ani-cli +++ b/ani-cli @@ -1,16 +1,17 @@ #!/bin/sh -use_external_menu=0 -launcher() { - [ "$use_external_menu" = "0" ] && fzf --prompt "$*" - [ "$use_external_menu" = "1" ] && external_menu "$*" -} +# UI external_menu() { rofi -dmenu -i -width 1500 -p "$*" } +launcher() { + [ "$use_external_menu" = "0" ] && fzf --prompt "$*" + [ "$use_external_menu" = "1" ] && external_menu "$*" +} + nth() { stdin=$(cat -) [ -z "$stdin" ] && return 1 @@ -25,19 +26,37 @@ die() { exit 1 } -# search the query and give results -search_anime() { - curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's|Show|\n|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*|\1\t\2 (\3 episode)|p" -} -# get the episodes list of the selected anime -episodes_list() { - curl -s "https://allanime.site/anime/$*" -A "$agent" | sed 's|\\||g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\n|g; s|"||g' -} +# SCRAPING -# get the embed urls of the selected episode -embed_urls() { - curl -s "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p' +# extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists +get_links() { + episode_link="$(curl -s "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -A "$agent" | sed 's|},{|\n|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p')" + case "$episode_link" in + *v.vrv.co*) + extract_link=$(printf "%s" "$episode_link" | grep "$subtitle" | cut -d'>' -f2) + curl -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr + ;; + *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|,|\n|g'); do + printf "%s >%s\n" "$j" "$extract_link" | sed "s|,[^/]*|${j}|g" + done | sort -nr + ;; + *//cache.*|*gofcdn.com*) + 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 -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|' | sed "s|>|>${relative_link}|g" | sort -nr + fi + ;; + *) + [ -n "$episode_link" ] && printf "%s\n" "$episode_link" + ;; + esac + printf "Fetching %s Links\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 @@ -74,44 +93,22 @@ generate_link() { [ -n "$provider_id" ] && get_links "$provider_id" } -# extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists -get_links() { - episode_link="$(curl -s "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -A "$agent" | sed 's|},{|\n|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p')" - case "$episode_link" in - *v.vrv.co*) - extract_link=$(printf "%s" "$episode_link" | grep "$subtitle" | cut -d'>' -f2) - curl -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr - ;; - *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|,|\n|g'); do - printf "%s >%s\n" "$j" "$extract_link" | sed "s|,[^/]*|${j}|g" - done | sort -nr - ;; - *//cache.*|*gofcdn.com*) - 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 -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|' | sed "s|>|>${relative_link}|g" | sort -nr - fi - ;; - *) - printf "%s" "$episode_link" - ;; - esac - printf "Fetching %s Links\n" "$provider_name" 1>&2 -} - select_quality () { links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g' | sort -g -r) case "$1" in best) - result=$(printf "%s" "$links" | head -n1) + result=$(cat "$cache_dir"/* | head -n1) + for qual in 1080 720 480 360 240 144; do + result_fixed=$(cat "$cache_dir"/* | grep -m 1 "$qual") + [ -n "$result_in" ] && result="$result_fixed" && break + done ;; worst) - result=$(printf "%s" "$links" | grep -E '^[0-9]{3,4}' | tail -n1) + result=$(cat "$cache_dir"/* | tail -n1) + for qual in 144 270 360 480 720 1080; do + result_fixed=$(cat "$cache_dir"/* | grep -m 1 "$qual") + [ -n "$result_in" ] && result="$result_fixed" && break + done ;; *) result=$(printf "%s" "$links" | grep -m 1 "$1") @@ -121,23 +118,10 @@ select_quality () { printf "%s" "$result" | cut -d'>' -f2 } -download () { - case $1 in - *m3u8*) - ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; - *) - aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; - esac -} - -# todo -# intent: player_function is set at program start, then the matching branch has the neccesary setup and the command. -# player_function can be a player program, download or debug. -play_episode() { - # clear cache - for file in "$cache_dir"/*; do - : > "$file" - done +# 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 + resp=$(curl -s "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p') # generate links into sequential files provider=1 i=0 @@ -150,6 +134,32 @@ play_episode() { # select the link with matching quality episode=$(select_quality "$quality") [ -z "$episode" ] && die "Episode not released!" +} + +# search the query and give results +search_anime() { + curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's|Show|\n|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*|\1\t\2 (\3 episode)|p" +} + +# get the episodes list of the selected anime +episodes_list() { + curl -s "https://allanime.site/anime/$*" -A "$agent" | sed 's|\\||g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\n|g; s|"||g' +} + + +# PLAYING + +download () { + case $1 in + *m3u8*) + ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; + *) + aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; + esac +} + +play_episode() { + get_episode_url #play the episode case "$player_function" in debug) @@ -175,20 +185,23 @@ play_episode() { esac } -# main + +# MAIN + +# setup agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" mode="sub" subtitle="en-US" download_dir="." quality="best" +player_function="debug" +use_external_menu=0 cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli" [ ! -d "$log_dir" ] && mkdir -p "$log_dir" - - +# searching query="$*" -player_function="mpv" while [ -z "$query" ]; do printf "Search anime\n>> " read -r query @@ -200,9 +213,8 @@ title=$(printf "%s" "$result" | cut -f2) allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" id=$(printf "%s" "$result" | cut -f1) ep_list=$(episodes_list "$id") - ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 -resp=$(embed_urls) +# playback & loop play_episode while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in @@ -221,7 +233,6 @@ while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode ;; esac [ -z "$ep_no" ] && die "Out of range" - resp=$(embed_urls) play_episode done From 187a8b66042c9c5b28140c6159d0c7eecefc28ad Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Wed, 28 Dec 2022 23:35:22 +0100 Subject: [PATCH 16/40] docs: updated PR template (might change further) Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- .github/PULL_REQUEST_TEMPLATE.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 23a0361b3..4ca5f7a4f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,15 +18,16 @@ - [ ] quality works - [ ] downloads work - [ ] quality works with downloads -- [ ] select episode -a and rapid resume work -- [ ] syncplay -s works +- [ ] select episode -a works +- [ ] vlc -v and syncplay -s work - [ ] autoplay, aka range selection, works +- [ ] history and -c work +- [ ] sub and dub both work +- [ ] all providers return links (not necessarily on a single anime, use debug mode to confirm) ## Additional Testcases - The safe bet: One Piece - Episode 0: Saenai Heroine no Sodatekata â™­ - Unicode: Saenai Heroine no Sodatekata â™­ -- Not uploaded: one piece dub episode 590 -- Unreleased: soredemo ayumu wa yosetekuru -- Short id (for decryption): Log Horizon episode 1-2 +- Non-whole episodes: Tensei shitara slime datta ken (ep. 24.5, ep. 24.9) From 6bc89359d0c0703518012f6858417325e364d917 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Fri, 30 Dec 2022 13:58:28 +0100 Subject: [PATCH 17/40] feat: script parameters can be set with env-vars + fix: quality selection backrank gogo links + feat: added sorting to rofi + fix: prevent external menu from opening during playing + fix: redirected stderr to stdout during playing + style: added | as delimeter to sed Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/ani-cli b/ani-cli index dedca7827..96107430b 100755 --- a/ani-cli +++ b/ani-cli @@ -4,7 +4,7 @@ # UI external_menu() { - rofi -dmenu -i -width 1500 -p "$*" + rofi -sort -dmenu -i -width 1500 -p "$*" } launcher() { @@ -34,7 +34,7 @@ get_links() { episode_link="$(curl -s "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -A "$agent" | sed 's|},{|\n|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p')" case "$episode_link" in *v.vrv.co*) - extract_link=$(printf "%s" "$episode_link" | grep "$subtitle" | cut -d'>' -f2) + extract_link=$(printf "%s" "$episode_link" | grep "$media_locale" | cut -d'>' -f2) curl -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr ;; *repackager.wixmp.com*) @@ -94,7 +94,7 @@ generate_link() { } select_quality () { - links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g' | sort -g -r) + links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g' | sort -g -r -s) case "$1" in best) result=$(cat "$cache_dir"/* | head -n1) @@ -151,10 +151,10 @@ episodes_list() { download () { case $1 in - *m3u8*) - ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; - *) - aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; + *m3u8*) + ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; + *) + aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; esac } @@ -168,13 +168,13 @@ play_episode() { printf "Selected link:\n%s\n" "$episode" ;; mpv | iina) - nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null & + nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & ;; vlc) - nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null & + nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & ;; syncplay) - nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" > /dev/null & + nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" > /dev/null 2>&1 & ;; download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" @@ -183,6 +183,7 @@ play_episode() { die "Invalid player function" ;; esac + [ "$use_external_menu" = "1" ] && wait } @@ -190,16 +191,17 @@ play_episode() { # setup agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0" -mode="sub" -subtitle="en-US" -download_dir="." -quality="best" -player_function="debug" -use_external_menu=0 -cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli" +mode="${ANI_CLI_MODE:-sub}" +media_locale="${ANI_CLI_MEDIA_LOCALE:-en-US}" +download_dir="${ANI_CLI_DOWNLOAD_DIR:-.}" +quality="${ANI_CLI_QUALITY:-best}" +player_function="${ANI_CLI_PLAYER:-debug}" +use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" +cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" -log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli" -[ ! -d "$log_dir" ] && mkdir -p "$log_dir" +hist_dir="${ANI_CLI_HIST_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli}" +[ ! -d "$hist_dir" ] && mkdir -p "$hist_dir" + # searching query="$*" while [ -z "$query" ]; do @@ -214,19 +216,20 @@ allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr id=$(printf "%s" "$result" | cut -f1) ep_list=$(episodes_list "$id") ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 + # playback & loop play_episode while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in previous) - ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null + ep_no=$(printf "%s" "$ep_list" | sed -n "|^${ep_no}$|{n;p;}") 2>/dev/null ;; replay) ;; next) - ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{g;1!p;};h") 2>/dev/null + 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: ") || exit 1 + ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") ;; *) exit 0 From 9e6907030157d27a57e167099c8aab317cb81d42 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Sat, 31 Dec 2022 01:41:18 +0100 Subject: [PATCH 18/40] feat: argument parsing + I possibly fucked up a merge conflict regarding quality parsing. + Please check if issues arise + refactor: folded single-line cases into one line + fix: show error if no results are found + fix: windows + docs: updated dependencies + docs: iamchokerman --> justchokingaround Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- .github/CODEOWNERS | 4 +- README.md | 5 +- ani-cli | 155 ++++++++++++++++++++------------------------- 3 files changed, 74 insertions(+), 90 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 113d69db7..4b898a249 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ * @port19x -/.assets/ @iamchokerman -README.md @iamchokerman +/.assets/ @justchokingaround +README.md @justchokingaround hist_transition.sh @Derisis13 diff --git a/README.md b/README.md index 388bf4c78..259319a9d 100644 --- a/README.md +++ b/README.md @@ -259,12 +259,13 @@ rm "$PREFIX/bin/ani-cli" - sed - awk - curl +- wget - openssl - mpv - Video Player - iina - mpv replacement for MacOS -- axel - Download manager +- aria2c - Download manager - ffmpeg - m3u8 Downloader -- fzf (optional) +- fzf - User interface ## Homies diff --git a/ani-cli b/ani-cli index 96107430b..744ba7074 100755 --- a/ani-cli +++ b/ani-cli @@ -26,6 +26,18 @@ die() { exit 1 } +help_info () { + die "To be implemented" +} + +version_info () { + die "To be implemented" +} + +update_script () { + die "To be implemented" +} + # SCRAPING @@ -52,9 +64,7 @@ get_links() { curl -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|' | sed "s|>|>${relative_link}|g" | sort -nr fi ;; - *) - [ -n "$episode_link" ] && printf "%s\n" "$episode_link" - ;; + *) [ -n "$episode_link" ] && printf "%s\n" "$episode_link" ;; esac printf "Fetching %s Links\n" "$provider_name" 1>&2 } @@ -68,26 +78,11 @@ provider_init() { # generates links based on given provider generate_link() { case $1 in - 1) - # vrv,wixmp(default)(m3u8)(multi) -> (mp4)(multi) - provider_init 'vrv|wixmp' '/Default :/p' - ;; - 2) - # pstatic(default backup)(mp4)(multi) - provider_init 'pstatic' '/Default B :/p' - ;; - 3) - # sharepoint(mp4)(single) - provider_init 'sharepoint' '/S-mp4 :/p' - ;; - 4) - # usercloud(mp4)(single) - provider_init 'usercloud' '/Uv-mp4 :/p' - ;; - *) - # gogoanime(m3u8)(multi) - provider_init 'gogoanime' '/Luf-mp4 :/p' - ;; + 1) provider_init 'vrv|wixmp' '/Default :/p' ;; # vrv,wixmp(default)(m3u8)(multi) -> (mp4)(multi) + 2) provider_init 'pstatic' '/Default B :/p' ;; # pstatic(default backup)(mp4)(multi) + 3) provider_init 'sharepoint' '/S-mp4 :/p' ;; # sharepoint(mp4)(single) + 4) provider_init 'usercloud' '/Uv-mp4 :/p' ;; # usercloud(mp4)(single) + *) provider_init 'gogoanime' '/Luf-mp4 :/p' ;; # gogoanime(m3u8)(multi) esac # logic yet to implement [ -n "$provider_id" ] && get_links "$provider_id" @@ -96,23 +91,9 @@ generate_link() { select_quality () { links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g' | sort -g -r -s) case "$1" in - best) - result=$(cat "$cache_dir"/* | head -n1) - for qual in 1080 720 480 360 240 144; do - result_fixed=$(cat "$cache_dir"/* | grep -m 1 "$qual") - [ -n "$result_in" ] && result="$result_fixed" && break - done - ;; - worst) - result=$(cat "$cache_dir"/* | tail -n1) - for qual in 144 270 360 480 720 1080; do - result_fixed=$(cat "$cache_dir"/* | grep -m 1 "$qual") - [ -n "$result_in" ] && result="$result_fixed" && break - done - ;; - *) - result=$(printf "%s" "$links" | grep -m 1 "$1") - ;; + 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" 1>&2 && result=$(cat "$cache_dir"/* | head -n1) printf "%s" "$result" | cut -d'>' -f2 @@ -138,7 +119,7 @@ get_episode_url() { # search the query and give results search_anime() { - curl -s "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -A "$agent" | sed 's|Show|\n|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*|\1\t\2 (\3 episode)|p" + wget -q -O - "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -U "$agent" | sed 's|Show|\n|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*|\1\t\2 (\3 episode)|p" } # get the episodes list of the selected anime @@ -151,37 +132,20 @@ episodes_list() { download () { case $1 in - *m3u8*) - ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; - *) - aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; + *m3u8*) ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; + *) aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; esac } play_episode() { get_episode_url - #play the episode case "$player_function" in - debug) - printf "All links:\n" - cat "$cache_dir"/* - printf "Selected link:\n%s\n" "$episode" - ;; - mpv | iina) - nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & - ;; - vlc) - nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & - ;; - syncplay) - nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" > /dev/null 2>&1 & - ;; - download) - "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" - ;; - *) - die "Invalid player function" - ;; + debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; + mpv | iina) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & ;; + vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & ;; + syncplay) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" > /dev/null 2>&1 & ;; + download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; + *) die "Invalid player function" ;; esac [ "$use_external_menu" = "1" ] && wait } @@ -201,16 +165,43 @@ cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" hist_dir="${ANI_CLI_HIST_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli}" [ ! -d "$hist_dir" ] && mkdir -p "$hist_dir" +search="${ANI_CLI_DEFAULT_SOURCE:-scrape}" + +while getopts ":vdsq:c-:" option ; do + case "$option" in + v) player_function=vlc ;; + d) player_function=download ;; + s) player_function=syncplay ;; + c) search=history ;; + q) quality="$OPTARG" ;; + -) + case "$OPTARG" in + version) version_info ;; + help) help_info ;; + update) update_script ;; + *) die "Unknown option!" ;; + esac + ;; + *) die "Unknown option!" ;; + esac +done +shift $(( OPTIND - 1 )) # searching -query="$*" -while [ -z "$query" ]; do - printf "Search anime\n>> " - read -r query -done -query=$(printf "%s" "$query" | sed "s| |%20|g") -anime_list=$(search_anime "$query") -result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 +case "$search" in +history) die "Not implemented yet!" ;; +*) + query="$*" + while [ -z "$query" ]; do + printf "Search anime\n>> " + read -r query + done + query=$(printf "%s" "$query" | sed "s| |%20|g") + anime_list=$(search_anime "$query") + [ -z "$anime_list" ] && die "No results found!" + result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 + ;; +esac title=$(printf "%s" "$result" | cut -f2) allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" id=$(printf "%s" "$result" | cut -f1) @@ -221,19 +212,11 @@ ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 play_episode while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in - previous) - ep_no=$(printf "%s" "$ep_list" | sed -n "|^${ep_no}$|{n;p;}") 2>/dev/null - ;; + previous) ep_no=$(printf "%s" "$ep_list" | sed -n "|^${ep_no}$|{n;p;}") 2>/dev/null ;; replay) ;; - next) - 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: ") - ;; - *) - exit 0 - ;; + next) 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: ") ;; + *) exit 0 ;; esac [ -z "$ep_no" ] && die "Out of range" play_episode From 7a06a9c1b92569bc55c013bf98e65243ce4a3f07 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Sun, 1 Jan 2023 21:31:44 +0100 Subject: [PATCH 19/40] feat: `--version` and `--help` now work Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/ani-cli b/ani-cli index 744ba7074..623ac8d40 100755 --- a/ani-cli +++ b/ani-cli @@ -1,5 +1,6 @@ #!/bin/sh +version_number="4.0" # UI @@ -27,11 +28,28 @@ die() { } help_info () { - die "To be implemented" + help_text="\ +ani-cli version $version_number + +Usage: +ani-cli [-v|-d|-s -q -c] +ani-cli [--version|--help|--update] + -v : use VLC as media player + -d : download instead of streaming + -s : use syncplay as media player + -q : set preferred quality + -c : get query from history (continue) + --version : print version number and exit + --help: print this and exit + --update: update the script from github and exit (this might require administrative privileges)\ +" + printf "%s\n" "$help_text" + exit 0 } version_info () { - die "To be implemented" + printf "%s\n" "$version_number" + exit 0 } update_script () { From f90d6eef8cb540766bc515017c82b61a6c383894 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Sun, 1 Jan 2023 22:36:51 +0100 Subject: [PATCH 20/40] fix: all links are now printed in debug mode + fix: remove | as delimiter to prev and next + refactor: new args implementation + curl --> wget Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 85 ++++++++++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/ani-cli b/ani-cli index 623ac8d40..2748cfee9 100755 --- a/ani-cli +++ b/ani-cli @@ -27,7 +27,7 @@ die() { exit 1 } -help_info () { +help_info() { help_text="\ ani-cli version $version_number @@ -47,25 +47,24 @@ ani-cli [--version|--help|--update] exit 0 } -version_info () { +version_info() { printf "%s\n" "$version_number" exit 0 } -update_script () { +update_script() { die "To be implemented" } - # SCRAPING # extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists get_links() { - episode_link="$(curl -s "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -A "$agent" | sed 's|},{|\n|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p')" + episode_link="$(wget -q -O - "https://blog.allanime.pro/apivtwo/clock.json?id=$*" -U "$agent" | sed 's|},{|\n|g' | sed -nE 's|.*link":"([^"]*)".*"resolutionStr":"([^"]*)".*|\2 >\1|p')" case "$episode_link" in *v.vrv.co*) extract_link=$(printf "%s" "$episode_link" | grep "$media_locale" | cut -d'>' -f2) - curl -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr + wget -q -O - "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr ;; *repackager.wixmp.com*) extract_link=$(printf "%s" "$episode_link" | cut -d'>' -f2 | sed 's|repackager.wixmp.com/||g;s|\.urlset.*||g') @@ -73,13 +72,13 @@ get_links() { printf "%s >%s\n" "$j" "$extract_link" | sed "s|,[^/]*|${j}|g" done | sort -nr ;; - *//cache.*|*gofcdn.com*) - if printf "%s" "$episode_link" | head -1 | grep -q "original.m3u";then + *//cache.* | *gofcdn.com*) + 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 -s "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|' | sed "s|>|>${relative_link}|g" | sort -nr + wget -q -O - "$extract_link" -U "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|' | sed "s|>|>${relative_link}|g" | sort -nr fi ;; *) [ -n "$episode_link" ] && printf "%s\n" "$episode_link" ;; @@ -96,18 +95,17 @@ provider_init() { # generates links based on given provider generate_link() { case $1 in - 1) provider_init 'vrv|wixmp' '/Default :/p' ;; # vrv,wixmp(default)(m3u8)(multi) -> (mp4)(multi) - 2) provider_init 'pstatic' '/Default B :/p' ;; # pstatic(default backup)(mp4)(multi) - 3) provider_init 'sharepoint' '/S-mp4 :/p' ;; # sharepoint(mp4)(single) - 4) provider_init 'usercloud' '/Uv-mp4 :/p' ;; # usercloud(mp4)(single) - *) provider_init 'gogoanime' '/Luf-mp4 :/p' ;; # gogoanime(m3u8)(multi) + 1) provider_init 'vrv|wixmp' '/Default :/p' ;; # vrv,wixmp(default)(m3u8)(multi) -> (mp4)(multi) + 2) provider_init 'pstatic' '/Default B :/p' ;; # pstatic(default backup)(mp4)(multi) + 3) provider_init 'sharepoint' '/S-mp4 :/p' ;; # sharepoint(mp4)(single) + 4) provider_init 'usercloud' '/Uv-mp4 :/p' ;; # usercloud(mp4)(single) + *) provider_init 'gogoanime' '/Luf-mp4 :/p' ;; # gogoanime(m3u8)(multi) esac # logic yet to implement [ -n "$provider_id" ] && get_links "$provider_id" } -select_quality () { - links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g' | sort -g -r -s) +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) ;; @@ -119,18 +117,19 @@ select_quality () { # 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 - resp=$(curl -s "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -A "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p') + # get the embed urls of the selected episode + resp=$(wget -q -O - "https://allanime.site/watch/$id/$allanime_title/episode-$ep_no-$mode" -U "$agent" | tr '{}' '\n' | sed 's|\\u002F|\/|g;s|\\||g' | sed -nE 's|.*sourceUrl":".*clock\?id=([^"]*)".*sourceName":"([^"]*)".*|\2 :\1|p') # generate links into sequential files provider=1 i=0 - while [ "$i" -lt 5 ]; do - generate_link "$provider" > "$cache_dir"/"$i" & + while [ "$i" -lt 5 ]; do + generate_link "$provider" >"$cache_dir"/"$i" & provider=$((provider % 5 + 1)) : $((i += 1)) done wait # select the link with matching quality + links=$(cat "$cache_dir"/* | sed 's|^Mp4-||g' | sort -g -r -s) episode=$(select_quality "$quality") [ -z "$episode" ] && die "Episode not released!" } @@ -142,13 +141,12 @@ search_anime() { # get the episodes list of the selected anime episodes_list() { - curl -s "https://allanime.site/anime/$*" -A "$agent" | sed 's|\\||g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\n|g; s|"||g' + wget -q -O - "https://allanime.site/anime/$*" -U "$agent" | sed 's|\\||g' | sed -nE "s|.*$mode\":\[([0-9.\",]*)\].*|\1|p" | sed 's|,|\n|g; s|"||g' } - # PLAYING -download () { +download() { case $1 in *m3u8*) ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; *) aria2c --check-certificate=false --summary-interval=0 -x 16 -s 16 "$1" --dir="$download_dir" -o "$2.mp4" --download-result=hide ;; @@ -159,16 +157,15 @@ play_episode() { get_episode_url case "$player_function" in debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; - mpv | iina) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & ;; - vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" > /dev/null 2>&1 & ;; - syncplay) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" > /dev/null 2>&1 & ;; + mpv | iina) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + syncplay) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; *) die "Invalid player function" ;; esac [ "$use_external_menu" = "1" ] && wait } - # MAIN # setup @@ -185,31 +182,25 @@ hist_dir="${ANI_CLI_HIST_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli}" [ ! -d "$hist_dir" ] && mkdir -p "$hist_dir" search="${ANI_CLI_DEFAULT_SOURCE:-scrape}" -while getopts ":vdsq:c-:" option ; do - case "$option" in - v) player_function=vlc ;; - d) player_function=download ;; - s) player_function=syncplay ;; - c) search=history ;; - q) quality="$OPTARG" ;; - -) - case "$OPTARG" in - version) version_info ;; - help) help_info ;; - update) update_script ;; - *) die "Unknown option!" ;; - esac - ;; - *) die "Unknown option!" ;; +while [ $# -gt 0 ]; do + case "$1" in + --vlc) player_function=vlc && shift ;; + -d) player_function=download && shift ;; + -s) player_function=syncplay && shift ;; + -c) search=history && shift ;; + -q) quality="$2" && shift 2 ;; + -v | -V | --version) version_info ;; + -h | --help) help_info ;; + -e | --episodes) episodes="$2" && shift 2 ;; + -u | -U | --update) update_script ;; + *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift && search="$query" ;; esac done -shift $(( OPTIND - 1 )) # searching case "$search" in history) die "Not implemented yet!" ;; *) - query="$*" while [ -z "$query" ]; do printf "Search anime\n>> " read -r query @@ -230,9 +221,9 @@ ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 play_episode while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in - previous) ep_no=$(printf "%s" "$ep_list" | sed -n "|^${ep_no}$|{n;p;}") 2>/dev/null ;; + previous) ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null ;; replay) ;; - next) ep_no=$(printf "%s" "$ep_list" | sed -n "|^${ep_no}$|{g;1!p;};h") 2>/dev/null ;; + next) 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: ") ;; *) exit 0 ;; esac From 78a3e207defd6bc38a3fd779a0243328e305e63e Mon Sep 17 00:00:00 2001 From: chokerman <44473782+justchokingaround@users.noreply.github.com> Date: Mon, 2 Jan 2023 18:20:08 +0100 Subject: [PATCH 21/40] feat: added dependency checker Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ani-cli b/ani-cli index 2748cfee9..9a407f7a9 100755 --- a/ani-cli +++ b/ani-cli @@ -56,6 +56,15 @@ update_script() { die "To be implemented" } +# checks if dependencies are present +dep_ch() { + for dep; do + if ! command -v "$dep" >/dev/null; then + die "Program \"$dep\" not found. Please install it." + fi + done +} + # SCRAPING # extract the video links from reponse of embed urls, extract mp4 links form m3u8 lists @@ -196,6 +205,13 @@ while [ $# -gt 0 ]; do *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift && search="$query" ;; esac done +printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n" +dep_ch "wget" "sed" "grep" || true +if [ "$player_function" = "download" ]; then + dep_ch "ffmpeg" "aria2c" +elif ! (uname -a | grep -qE '[Aa]ndroid'); then + [ "$player_function" != "debug" ] && [ "$player_function" != "download" ] && dep_ch "$player_function" +fi # searching case "$search" in @@ -211,6 +227,8 @@ history) die "Not implemented yet!" ;; result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 ;; esac +# moves the cursor up one line and clears that line +tput cuu1 && tput el title=$(printf "%s" "$result" | cut -f2) allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" id=$(printf "%s" "$result" | cut -f1) From ef33ccdb08ae324c548ac32d03ec0343b4a96990 Mon Sep 17 00:00:00 2001 From: chokerman <44473782+justchokingaround@users.noreply.github.com> Date: Mon, 2 Jan 2023 18:30:56 +0100 Subject: [PATCH 22/40] feat: episode arg Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ani-cli b/ani-cli index 9a407f7a9..c7e2730b2 100755 --- a/ani-cli +++ b/ani-cli @@ -200,7 +200,7 @@ while [ $# -gt 0 ]; do -q) quality="$2" && shift 2 ;; -v | -V | --version) version_info ;; -h | --help) help_info ;; - -e | --episodes) episodes="$2" && shift 2 ;; + -e | --episodes) ep_no="$2" && shift 2 ;; -u | -U | --update) update_script ;; *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift && search="$query" ;; esac @@ -233,7 +233,8 @@ title=$(printf "%s" "$result" | cut -f2) allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" id=$(printf "%s" "$result" | cut -f1) ep_list=$(episodes_list "$id") -ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") || exit 1 +[ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") +[ -z "$ep_no" ] && exit 1 # playback & loop play_episode From 359e7e941822c285ca787815e44a9cf87251b2e5 Mon Sep 17 00:00:00 2001 From: chokerman <44473782+justchokingaround@users.noreply.github.com> Date: Mon, 2 Jan 2023 18:37:02 +0100 Subject: [PATCH 23/40] feat: self-update + fix typo in update function + docs: help_info + feat: OSX and windows player functions Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 86 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/ani-cli b/ani-cli index c7e2730b2..290b1e897 100755 --- a/ani-cli +++ b/ani-cli @@ -28,23 +28,36 @@ die() { } help_info() { - help_text="\ -ani-cli version $version_number + printf " + Usage: + %s [options] [query] + %s [query] [options] + %s [options] [query] [options] -Usage: -ani-cli [-v|-d|-s -q -c] -ani-cli [--version|--help|--update] - -v : use VLC as media player - -d : download instead of streaming - -s : use syncplay as media player - -q : set preferred quality - -c : get query from history (continue) - --version : print version number and exit - --help: print this and exit - --update: update the script from github and exit (this might require administrative privileges)\ -" - printf "%s\n" "$help_text" - exit 0 + Options: + -c, --continue + Continue watching from history + -d, --download + Download the video instead of playing it + -s, --syncplay + Use Syncplay to watch with friends + -q, --quality + Specify the video quality + --vlc + Use VLC to play the video + -v, -V, --version + Show the version of the script + -h, --help + Show this help message and exit + -e, --episode + Specify the number of episodes to watch + -u, -U, --update + Update the script + Some example usages: + %s -q 720p banana fish + %s -d -e 2 cyberpunk edgerunners + %s --vlc cyberpunk edgerunners -q 1080p -e 4 + \n" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" } version_info() { @@ -53,7 +66,18 @@ version_info() { } update_script() { - die "To be implemented" + update="$(curl -A "$agent" -s "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 @@ -168,7 +192,7 @@ play_episode() { debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; mpv | iina) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - syncplay) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; + *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; *) die "Invalid player function" ;; esac @@ -183,7 +207,10 @@ mode="${ANI_CLI_MODE:-sub}" media_locale="${ANI_CLI_MEDIA_LOCALE:-en-US}" download_dir="${ANI_CLI_DOWNLOAD_DIR:-.}" quality="${ANI_CLI_QUALITY:-best}" -player_function="${ANI_CLI_PLAYER:-debug}" +case "$(uname)" in + Darwin*) player_function="${ANI_CLI_PLAYER:-iina}" ;; + *) player_function="${ANI_CLI_PLAYER:-mpv}" ;; +esac use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" @@ -194,15 +221,22 @@ search="${ANI_CLI_DEFAULT_SOURCE:-scrape}" while [ $# -gt 0 ]; do case "$1" in --vlc) player_function=vlc && shift ;; - -d) player_function=download && shift ;; - -s) player_function=syncplay && shift ;; - -c) search=history && shift ;; - -q) quality="$2" && shift 2 ;; + -s | --syncplay) + case "$(uname -s)" in + Darwin*) player_function="/Applications/Syncplay.app/Contents/MacOS/syncplay" ;; + MINGW*|*Msys) player_function="/c/Program Files (x86)/Syncplay/Syncplay.exe" ;; + *) player_function="syncplay" ;; + esac + shift + ;; + -q | --quality) quality="$2" && shift 2 ;; + -c | --continue) search=history && shift ;; + -d | --download) player_function=download && shift ;; -v | -V | --version) version_info ;; - -h | --help) help_info ;; - -e | --episodes) ep_no="$2" && shift 2 ;; + -h | --help) help_info && exit 0 ;; + -e | --episode) ep_no="$2" && shift 2 ;; -u | -U | --update) update_script ;; - *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift && search="$query" ;; + *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift ;; esac done printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n" From 82d2182ddc2778503c8ba6d1a7b6ba253922bf01 Mon Sep 17 00:00:00 2001 From: Xxusername <34575742+Nannk@users.noreply.github.com> Date: Tue, 3 Jan 2023 11:48:20 +0100 Subject: [PATCH 24/40] :shrug: <- don't do commits like this pls leaves the commitmessage uninformative :chad-emote: * Eyo wtf is this wall of text of a commit message * * changed to ani-cli v4 and added checkups if mpv is from flathub * flatpak, not flatpack * check implemented in #setup instead of play_episode function * nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * bug: nohup starts flatpak with ('') * hopefully fixed a bug where nohup will try to execute flatpak option as one command * changed player_function to always be an env var + fix:iina working again + fix: dep_ch working again Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ani-cli b/ani-cli index 290b1e897..5e05e6681 100755 --- a/ani-cli +++ b/ani-cli @@ -84,6 +84,7 @@ update_script() { dep_ch() { for dep; do if ! command -v "$dep" >/dev/null; then + [ "$player_function" = "flatpak_mpv" ] && ! flatpak info io.mpv.Mpv 2>&1 | grep -q 'error' && continue die "Program \"$dep\" not found. Please install it." fi done @@ -190,7 +191,9 @@ play_episode() { get_episode_url case "$player_function" in debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; - mpv | iina) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + mpv) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; @@ -208,9 +211,14 @@ media_locale="${ANI_CLI_MEDIA_LOCALE:-en-US}" download_dir="${ANI_CLI_DOWNLOAD_DIR:-.}" quality="${ANI_CLI_QUALITY:-best}" case "$(uname)" in - Darwin*) player_function="${ANI_CLI_PLAYER:-iina}" ;; - *) player_function="${ANI_CLI_PLAYER:-mpv}" ;; +Darwin*) player_function="${ANI_CLI_PLAYER:-iina}" ;; +*) if uname -a | grep -q 'steamdeck'; then + player_function="${ANI_CLI_PLAYER:-flatpak_mpv}" +else + player_function="${ANI_CLI_PLAYER:-mpv}" +fi ;; esac + use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" @@ -223,9 +231,9 @@ while [ $# -gt 0 ]; do --vlc) player_function=vlc && shift ;; -s | --syncplay) case "$(uname -s)" in - Darwin*) player_function="/Applications/Syncplay.app/Contents/MacOS/syncplay" ;; - MINGW*|*Msys) player_function="/c/Program Files (x86)/Syncplay/Syncplay.exe" ;; - *) player_function="syncplay" ;; + Darwin*) player_function="/Applications/Syncplay.app/Contents/MacOS/syncplay" ;; + MINGW* | *Msys) player_function="/c/Program Files (x86)/Syncplay/Syncplay.exe" ;; + *) player_function="syncplay" ;; esac shift ;; From 46704795083577b80b7b4f089ffb267b729fc747 Mon Sep 17 00:00:00 2001 From: coolnsx Date: Tue, 10 Jan 2023 13:24:42 +0530 Subject: [PATCH 25/40] feat: better player support * This rebase has been sponsored by magit * + fixed indentation of player_function case + SC fix Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/ani-cli b/ani-cli index 5e05e6681..e1a953ae0 100755 --- a/ani-cli +++ b/ani-cli @@ -190,14 +190,16 @@ download() { play_episode() { get_episode_url case "$player_function" in - debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; - mpv) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; - download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; - *) die "Invalid player function" ;; + debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; + mpv|mpv.exe) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + mpv_android) nohup am start --user 0 -a android.intent.action.VIEW -d "$episode" -n is.xyz.mpv/.MPVActivity >/dev/null 2>&1 & ;; + vlc_android) 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}-${mode}" >/dev/null 2>&1 & ;; + iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; + download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; + *) nohup "$player_function" >/dev/null 2>&1 & ;; esac [ "$use_external_menu" = "1" ] && wait } @@ -210,13 +212,12 @@ mode="${ANI_CLI_MODE:-sub}" media_locale="${ANI_CLI_MEDIA_LOCALE:-en-US}" download_dir="${ANI_CLI_DOWNLOAD_DIR:-.}" quality="${ANI_CLI_QUALITY:-best}" -case "$(uname)" in -Darwin*) player_function="${ANI_CLI_PLAYER:-iina}" ;; -*) if uname -a | grep -q 'steamdeck'; then - player_function="${ANI_CLI_PLAYER:-flatpak_mpv}" -else - player_function="${ANI_CLI_PLAYER:-mpv}" -fi ;; +case "$(uname -a)" in +*Darwin*) player_function="${ANI_CLI_PLAYER:-iina}" ;; # mac OS +*ndroid*) player_function="${ANI_CLI_PLAYER:-mpv_android}" ;; # Android OS +*steamdeck*) player_function="${ANI_CLI_PLAYER:-flatpak_mpv}" ;; # steamdeck OS +*MINGW*) player_function="${ANI_CLI_PLAYER:-mpv.exe}" ;; # Windows OS +*) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS esac use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" From fe08bfaa08509ea1681fd511a175bff6db4afe69 Mon Sep 17 00:00:00 2001 From: coolnsx Date: Tue, 10 Jan 2023 15:58:34 +0530 Subject: [PATCH 26/40] feat: vlc player support for all OS + chore: indentations + fix: arguments resemble v3 behavior + fix: dependency checking Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 71 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/ani-cli b/ani-cli index e1a953ae0..7e4e8e9ab 100755 --- a/ani-cli +++ b/ani-cli @@ -43,15 +43,15 @@ help_info() { Use Syncplay to watch with friends -q, --quality Specify the video quality - --vlc + -v, --vlc Use VLC to play the video - -v, -V, --version + -V, --version Show the version of the script -h, --help Show this help message and exit -e, --episode Specify the number of episodes to watch - -u, -U, --update + -U, --update Update the script Some example usages: %s -q 720p banana fish @@ -66,7 +66,7 @@ version_info() { } update_script() { - update="$(curl -A "$agent" -s "https://raw.githubusercontent.com/pystardust/ani-cli/master/ani-cli")" || die "Connection error" + update="$(wget -q -O - -U "$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" @@ -83,10 +83,7 @@ update_script() { # checks if dependencies are present dep_ch() { for dep; do - if ! command -v "$dep" >/dev/null; then - [ "$player_function" = "flatpak_mpv" ] && ! flatpak info io.mpv.Mpv 2>&1 | grep -q 'error' && continue - die "Program \"$dep\" not found. Please install it." - fi + command -v "$dep" >/dev/null || die "Program \"$dep\" not found. Please install it." done } @@ -190,16 +187,16 @@ download() { play_episode() { get_episode_url case "$player_function" in - debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; - mpv|mpv.exe) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - mpv_android) nohup am start --user 0 -a android.intent.action.VIEW -d "$episode" -n is.xyz.mpv/.MPVActivity >/dev/null 2>&1 & ;; - vlc_android) 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}-${mode}" >/dev/null 2>&1 & ;; - iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - vlc) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; - download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; - *) nohup "$player_function" >/dev/null 2>&1 & ;; + debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; + mpv*) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + 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}-${mode}" >/dev/null 2>&1 & ;; + iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + vlc*) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; + download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; + *) nohup "$player_function" >/dev/null 2>&1 & ;; esac [ "$use_external_menu" = "1" ] && wait } @@ -213,11 +210,11 @@ media_locale="${ANI_CLI_MEDIA_LOCALE:-en-US}" 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:-mpv_android}" ;; # Android OS -*steamdeck*) player_function="${ANI_CLI_PLAYER:-flatpak_mpv}" ;; # steamdeck OS -*MINGW*) player_function="${ANI_CLI_PLAYER:-mpv.exe}" ;; # Windows OS -*) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS + *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*) player_function="${ANI_CLI_PLAYER:-mpv.exe}" ;; # Windows OS + *) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS esac use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" @@ -229,7 +226,14 @@ search="${ANI_CLI_DEFAULT_SOURCE:-scrape}" while [ $# -gt 0 ]; do case "$1" in - --vlc) player_function=vlc && shift ;; + -v | --vlc) + case "$(uname -a)" in + *ndroid*) player_function="android_vlc" ;; + MINGW*) player_function="vlc.exe" ;; + *) player_function="vlc" ;; + esac + shift + ;; -s | --syncplay) case "$(uname -s)" in Darwin*) player_function="/Applications/Syncplay.app/Contents/MacOS/syncplay" ;; @@ -241,20 +245,25 @@ while [ $# -gt 0 ]; do -q | --quality) quality="$2" && shift 2 ;; -c | --continue) search=history && shift ;; -d | --download) player_function=download && shift ;; - -v | -V | --version) version_info ;; + -V | --version) version_info ;; -h | --help) help_info && exit 0 ;; -e | --episode) ep_no="$2" && shift 2 ;; - -u | -U | --update) update_script ;; + -U | --update) update_script ;; *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift ;; esac done printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n" dep_ch "wget" "sed" "grep" || true -if [ "$player_function" = "download" ]; then - dep_ch "ffmpeg" "aria2c" -elif ! (uname -a | grep -qE '[Aa]ndroid'); then - [ "$player_function" != "debug" ] && [ "$player_function" != "download" ] && dep_ch "$player_function" -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 "Dependency checking of players on Android is disabled\n" ;; + *) dep_ch "$player_function" ;; +esac # searching case "$search" in From ac954101c09c8b5254f67d584c7d8b44208121e4 Mon Sep 17 00:00:00 2001 From: coolnsx Date: Tue, 10 Jan 2023 16:18:55 +0530 Subject: [PATCH 27/40] fix: -A -> -U in wget Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ani-cli b/ani-cli index 7e4e8e9ab..c64fb9c18 100755 --- a/ani-cli +++ b/ani-cli @@ -95,7 +95,7 @@ get_links() { case "$episode_link" in *v.vrv.co*) extract_link=$(printf "%s" "$episode_link" | grep "$media_locale" | cut -d'>' -f2) - wget -q -O - "$extract_link" -A "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr + wget -q -O - "$extract_link" -U "$agent" | sed 's|^#.*x||g; s|,.*|p|g; /^#/d; $!N; s|\n| >|; s|\/index-v1-a1\.m3u8||g' | sort -nr ;; *repackager.wixmp.com*) extract_link=$(printf "%s" "$episode_link" | cut -d'>' -f2 | sed 's|repackager.wixmp.com/||g;s|\.urlset.*||g') From d7988ffb901b604cd515d77efb38b3d17bd3920c Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Tue, 10 Jan 2023 21:20:48 +0100 Subject: [PATCH 28/40] feat: use external menu for query * One might think my criteria for grouping commits is completely random. Rest assured, it's only arbitrary, not random. * + fix: error handling for external menu query + fix: missing check for fzf (#971) + fix: external menu for searching + fix: stray \ in grep Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 65 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/ani-cli b/ani-cli index c64fb9c18..a0bfa4d53 100755 --- a/ani-cli +++ b/ani-cli @@ -167,7 +167,7 @@ get_episode_url() { # search the query and give results search_anime() { - wget -q -O - "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -U "$agent" | sed 's|Show|\n|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"([^\"]*)\".*\"$mode\":([1-9][^,]*).*|\1\t\2 (\3 episode)|p" + wget -q -O - "https://allanime.site/allanimeapi?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Atrue%2C%22allowUnknown%22%3Atrue%2C%22query%22%3A%22$*%22%7D%2C%22limit%22%3A40%2C%22page%22%3A1%2C%22translationType%22%3A%22$mode%22%2C%22countryOrigin%22%3A%22ALL%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229c7a8bc1e095a34f2972699e8105f7aaf9082c6e1ccd56eab99c2f1a971152c6%22%7D%7D" -U "$agent" | sed 's|Show|\n|g' | sed -nE "s|.*_id\":\"([^\"]*)\",\"name\":\"(.*)\",\"english.*\"$mode\":([1-9][^,]*).*|\1\t\2 (\3 episode)|p" | sed 's/\\//g;s/"//g' } # get the episodes list of the selected anime @@ -187,16 +187,16 @@ download() { play_episode() { get_episode_url case "$player_function" in - debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; - mpv*) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - 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}-${mode}" >/dev/null 2>&1 & ;; - iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - vlc*) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; - *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; - download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; - *) nohup "$player_function" >/dev/null 2>&1 & ;; + debug) printf "All links:\n%s\nSelected link:\n%s\n" "$links" "$episode" ;; + mpv*) nohup "$player_function" --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + 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}-${mode}" >/dev/null 2>&1 & ;; + iina) nohup "$player_function" --no-stdin --keep-running --mpv-force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + flatpak_mpv) flatpak run io.mpv.Mpv --force-media-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + vlc*) nohup "$player_function" --video-title="${allanime_title}episode-${ep_no}-${mode}" "$episode" >/dev/null 2>&1 & ;; + *yncpla*) nohup "$player_function" "$episode" -- --force-media-title="${allanime_title}episode-${ep_no}-${mode}" >/dev/null 2>&1 & ;; + download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; + *) nohup "$player_function" >/dev/null 2>&1 & ;; esac [ "$use_external_menu" = "1" ] && wait } @@ -210,11 +210,11 @@ media_locale="${ANI_CLI_MEDIA_LOCALE:-en-US}" 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*) player_function="${ANI_CLI_PLAYER:-mpv.exe}" ;; # Windows OS - *) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS +*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*) player_function="${ANI_CLI_PLAYER:-mpv.exe}" ;; # Windows OS +*) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS esac use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" @@ -226,11 +226,11 @@ search="${ANI_CLI_DEFAULT_SOURCE:-scrape}" while [ $# -gt 0 ]; do case "$1" in - -v | --vlc) + -v | --vlc) case "$(uname -a)" in - *ndroid*) player_function="android_vlc" ;; - MINGW*) player_function="vlc.exe" ;; - *) player_function="vlc" ;; + *ndroid*) player_function="android_vlc" ;; + MINGW*) player_function="vlc.exe" ;; + *) player_function="vlc" ;; esac shift ;; @@ -253,16 +253,16 @@ while [ $# -gt 0 ]; do esac done printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n" -dep_ch "wget" "sed" "grep" || true +dep_ch "wget" "sed" "grep" "fzf" || true 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 "Dependency checking of players on Android is disabled\n" ;; - *) dep_ch "$player_function" ;; +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 "Dependency checking of players on Android is disabled\n" ;; +*) dep_ch "$player_function" ;; esac # searching @@ -270,8 +270,11 @@ case "$search" in history) die "Not implemented yet!" ;; *) while [ -z "$query" ]; do - printf "Search anime\n>> " - read -r query + if [ "$use_external_menu" = "0" ]; then + printf "Search anime: " && read -r query + else + query=$(printf "" | external_menu "Search anime: ") + fi done query=$(printf "%s" "$query" | sed "s| |%20|g") anime_list=$(search_anime "$query") From df690dc2be9dd2557017762ddb14c070faff384b Mon Sep 17 00:00:00 2001 From: port19 Date: Fri, 13 Jan 2023 11:10:15 +0100 Subject: [PATCH 29/40] docs: add back zenith as maintainer Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- README.md | 1 + ani-cli | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 259319a9d..0a45dc4bf 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ +

diff --git a/ani-cli b/ani-cli index a0bfa4d53..822b19c47 100755 --- a/ani-cli +++ b/ani-cli @@ -142,7 +142,7 @@ select_quality() { 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" 1>&2 && result=$(cat "$cache_dir"/* | head -n1) + [ -z "$result" ] && printf "Specified quality not found, defaulting to best" 1>&2 && result=$(printf "%s" "$links" | head -n1) printf "%s" "$result" | cut -d'>' -f2 } From 471d33c0cb6cc787dcf1e98f8b8eab0cd48d1c7f Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Fri, 13 Jan 2023 23:32:28 +0100 Subject: [PATCH 30/40] feat: history Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/ani-cli b/ani-cli index 822b19c47..b66b8fd69 100755 --- a/ani-cli +++ b/ani-cli @@ -177,6 +177,22 @@ episodes_list() { # PLAYING +process_hist_entry() { + ep_list=$(episodes_list "$id") + ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{g;1!p;};h") 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*) ffmpeg -loglevel error -stats -i "$1" -c copy "$download_dir/$2.mp4" ;; @@ -198,6 +214,7 @@ play_episode() { download) "$player_function" "$episode" "${allanime_title}episode-${ep_no}-${mode}" ;; *) nohup "$player_function" >/dev/null 2>&1 & ;; esac + update_history [ "$use_external_menu" = "1" ] && wait } @@ -222,6 +239,8 @@ cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" 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 @@ -267,7 +286,20 @@ esac # searching case "$search" in -history) die "Not implemented yet!" ;; +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 histroy!" + result=$(printf "%s" "$anime_list" | nth "Select anime: " | cut -f1) + result=$(grep "$result" "$histfile") + read -r ep_no id title <<-EOF + $result + EOF + ep_list=$(episodes_list "$id") + ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{g;1!p;};h") 2>/dev/null + allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" + tput cuu1 && tput el + ;; *) while [ -z "$query" ]; do if [ "$use_external_menu" = "0" ]; then @@ -280,16 +312,16 @@ history) die "Not implemented yet!" ;; anime_list=$(search_anime "$query") [ -z "$anime_list" ] && die "No results found!" result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 + # moves the cursor up one line and clears that line + tput cuu1 && tput el + title=$(printf "%s" "$result" | cut -f2) + allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" + id=$(printf "%s" "$result" | cut -f1) + ep_list=$(episodes_list "$id") + [ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") + [ -z "$ep_no" ] && exit 1 ;; esac -# moves the cursor up one line and clears that line -tput cuu1 && tput el -title=$(printf "%s" "$result" | cut -f2) -allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" -id=$(printf "%s" "$result" | cut -f1) -ep_list=$(episodes_list "$id") -[ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") -[ -z "$ep_no" ] && exit 1 # playback & loop play_episode From 8bb2034386a660178daf048551e6a224e2b2af5c Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Fri, 13 Jan 2023 23:44:22 +0100 Subject: [PATCH 31/40] fix: die when optarg is missing from `-q` or `-e` Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ani-cli b/ani-cli index b66b8fd69..b1bd2bf62 100755 --- a/ani-cli +++ b/ani-cli @@ -261,12 +261,20 @@ while [ $# -gt 0 ]; do esac shift ;; - -q | --quality) quality="$2" && shift 2 ;; + -q | --quality) + [ $# -lt 2 ] && die "missing argument!" + quality="$2" + shift 2 + ;; -c | --continue) search=history && shift ;; -d | --download) player_function=download && shift ;; -V | --version) version_info ;; -h | --help) help_info && exit 0 ;; - -e | --episode) ep_no="$2" && shift 2 ;; + -e | --episode) + [ $# -lt 2 ] && die "missing argument!" + ep_no="$2" + shift 2 + ;; -U | --update) update_script ;; *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift ;; esac From a459f1ed3e33031fdaee7d8db2f1424b7fbd75d5 Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Fri, 13 Jan 2023 23:56:49 +0100 Subject: [PATCH 32/40] feat: option for dub mode Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ani-cli b/ani-cli index b1bd2bf62..f2496ea65 100755 --- a/ani-cli +++ b/ani-cli @@ -51,6 +51,8 @@ help_info() { Show this help message and exit -e, --episode Specify the number of episodes to watch + --dub + play dubbed version -U, --update Update the script Some example usages: @@ -58,6 +60,7 @@ help_info() { %s -d -e 2 cyberpunk edgerunners %s --vlc cyberpunk edgerunners -q 1080p -e 4 \n" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" + exit 0 } version_info() { @@ -251,7 +254,6 @@ while [ $# -gt 0 ]; do MINGW*) player_function="vlc.exe" ;; *) player_function="vlc" ;; esac - shift ;; -s | --syncplay) case "$(uname -s)" in @@ -259,25 +261,26 @@ while [ $# -gt 0 ]; do MINGW* | *Msys) player_function="/c/Program Files (x86)/Syncplay/Syncplay.exe" ;; *) player_function="syncplay" ;; esac - shift ;; -q | --quality) [ $# -lt 2 ] && die "missing argument!" quality="$2" - shift 2 + shift ;; - -c | --continue) search=history && shift ;; - -d | --download) player_function=download && shift ;; + -c | --continue) search=history ;; + -d | --download) player_function=download ;; -V | --version) version_info ;; - -h | --help) help_info && exit 0 ;; + -h | --help) help_info;; -e | --episode) [ $# -lt 2 ] && die "missing argument!" ep_no="$2" - shift 2 + shift ;; + --dub) mode="dub" ;; -U | --update) update_script ;; - *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" && shift ;; + *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" ;; esac + shift done printf "\33[2K\r\033[1;34mChecking dependencies...\033[0m\n" dep_ch "wget" "sed" "grep" "fzf" || true From 262e9e9e7c107df64f41d09589e2601b8d9e930c Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Tue, 17 Jan 2023 13:43:05 +0100 Subject: [PATCH 33/40] feat: range selection Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 66 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/ani-cli b/ani-cli index f2496ea65..d6a822a3a 100755 --- a/ani-cli +++ b/ani-cli @@ -5,12 +5,13 @@ version_number="4.0" # UI external_menu() { - rofi -sort -dmenu -i -width 1500 -p "$*" + rofi "$1" -sort -dmenu -i -width 1500 -p "$2" } launcher() { - [ "$use_external_menu" = "0" ] && fzf --prompt "$*" - [ "$use_external_menu" = "1" ] && external_menu "$*" + [ "$use_external_menu" = "0" ] && [ -z "$1" ] && set -- "+m" "$2" + [ "$use_external_menu" = "0" ] && fzf "$1" --reverse --prompt "$2" + [ "$use_external_menu" = "1" ] && external_menu "$1" "$2" } nth() { @@ -18,7 +19,10 @@ nth() { [ -z "$stdin" ] && return 1 line_count="$(printf "%s\n" "$stdin" | wc -l)" [ "$line_count" -eq 1 ] && printf "%s" "$stdin" && return 0 - line=$(printf "%s" "$stdin" | sed 's|.*\t||' | launcher "$*") + prompt="$1" + multi_flag="" + [ $# -ne 1 ] && shift && multi_flag="$1" + line=$(printf "%s" "$stdin" | sed 's|.*\t||' | launcher "$multi_flag" "$prompt") [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" || exit 1 } @@ -49,7 +53,7 @@ help_info() { Show the version of the script -h, --help Show this help message and exit - -e, --episode + -e, --episode, -r, --range Specify the number of episodes to watch --dub play dubbed version @@ -59,7 +63,9 @@ help_info() { %s -q 720p banana fish %s -d -e 2 cyberpunk edgerunners %s --vlc cyberpunk edgerunners -q 1080p -e 4 - \n" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" + %s blue lock -e 5-6 + %s -e \"5 6\" blue lock + \n" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" "${0##*/}" exit 0 } @@ -188,10 +194,10 @@ process_hist_entry() { update_history() { if grep -q -- "$id" "$histfile"; then - sed -E "s/^[^\t]+\t${id}\t/${ep_no}\t${id}\t/" "$histfile" > "${histfile}.new" + 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" + printf "%s\t%s\t%s\n" "$ep_no" "$id" "$title" >>"${histfile}.new" fi mv "${histfile}.new" "$histfile" } @@ -238,12 +244,14 @@ case "$(uname -a)" in esac use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" +[ "$use_external_menu" = "0" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-${multi_selection_flag:-"-m"}}" +[ "$use_external_menu" = "1" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-${multi_selection_flag:-"-multi-select"}}" cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" 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" +[ ! -f "$histfile" ] && : >"$histfile" search="${ANI_CLI_DEFAULT_SOURCE:-scrape}" while [ $# -gt 0 ]; do @@ -262,7 +270,7 @@ while [ $# -gt 0 ]; do *) player_function="syncplay" ;; esac ;; - -q | --quality) + -q | --quality) [ $# -lt 2 ] && die "missing argument!" quality="$2" shift @@ -270,8 +278,8 @@ while [ $# -gt 0 ]; do -c | --continue) search=history ;; -d | --download) player_function=download ;; -V | --version) version_info ;; - -h | --help) help_info;; - -e | --episode) + -h | --help) help_info ;; + -e | --episode | -r | --range) [ $# -lt 2 ] && die "missing argument!" ep_no="$2" shift @@ -298,13 +306,13 @@ esac # searching case "$search" in history) - anime_list=$(while read -r ep_no id title; do process_hist_entry & done < "$histfile") + anime_list=$(while read -r ep_no id title; do process_hist_entry & done <"$histfile") wait [ -z "$anime_list" ] && die "No unwatched series in histroy!" result=$(printf "%s" "$anime_list" | nth "Select anime: " | cut -f1) result=$(grep "$result" "$histfile") read -r ep_no id title <<-EOF - $result + $result EOF ep_list=$(episodes_list "$id") ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{g;1!p;};h") 2>/dev/null @@ -313,7 +321,7 @@ history) ;; *) while [ -z "$query" ]; do - if [ "$use_external_menu" = "0" ]; then + if [ "$use_external_menu" = "0" ]; then printf "Search anime: " && read -r query else query=$(printf "" | external_menu "Search anime: ") @@ -329,13 +337,35 @@ history) allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" id=$(printf "%s" "$result" | cut -f1) ep_list=$(episodes_list "$id") - [ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: ") + [ -z "$ep_no" ] && ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$multi_selection_flag") [ -z "$ep_no" ] && exit 1 ;; esac # playback & loop -play_episode +play() { + start=$(printf "%s" "$ep_no" | sed "s/[^0-9]*\([0-9]*\).*/\1/") + end=$(printf "%s" "$ep_no" | sed "s/[^0-9]*[0-9]*[^0-9]*\([0-9]*\).*/\1/") + [ -z "$start" ] || [ -z "$end" ] && unset start end + line_count=$(printf "%s\n" "$ep_no" | wc -l) + if [ "$line_count" != 1 ] || [ -n "$start" ] || [ -n "$end" ]; then + [ -z "$start" ] && start=$(printf "%s\n" "$ep_no" | tail -n1) + [ -z "$end" ] && end=$(printf "%s\n" "$ep_no" | head -n1) + range=$(printf "%s\n" "$ep_list" | sed '1!G;h;$!d' | sed -nE "/^${start}\$/,/^${end}\$/p") + [ -z "$range" ] && die "Invalid range!" + for i in $range; do + ep_no=$i + printf "\33[2K\r\033[1;34mPlaying episode %s...\033[0m\n" "$ep_no" + play_episode + tput clear + done + else + play_episode + fi +} +play +[ "$player_function" = "download" ] && exit 0 + while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in previous) ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null ;; @@ -345,7 +375,7 @@ while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode *) exit 0 ;; esac [ -z "$ep_no" ] && die "Out of range" - play_episode + play done # ani-cli From ad9dac6eb30107bc2f881fe5147b17db88badebd Mon Sep 17 00:00:00 2001 From: Xxusername <34575742+Nannk@users.noreply.github.com> Date: Sat, 21 Jan 2023 08:19:58 +0100 Subject: [PATCH 34/40] steamdeck readme stuff (#976) Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- README.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/README.md b/README.md index 0a45dc4bf..20e085ee4 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ https://user-images.githubusercontent.com/44473782/160729779-41fe207c-b5aa-4fed- - [MacOS](#MacOS) - [Windows](#Windows) - [Android](#Android) + - [Steam Deck](#steam-deck) - [Uninstall](#Uninstall) - [Dependencies](#Dependencies) - [Homies](#Homies) @@ -203,6 +204,93 @@ You need to add any referrer in mpv by opening mpv [(playstore version)](https:/ referrer="https://animixplay.to/" ``` +### Steam Deck + +#### Copypaste script: + +* Switch to Desktop mode (`STEAM` Button > Power > Switch to Desktop) +* Open `Konsole` (Steam Deck Icon in bottom left corner > System > Konsole) +* Copy the script, paste it in the CLI and press Enter("A" button on Steam Deck) + +``` +[ ! -d ~/.local/bin ] && mkdir ~/.local/bin && echo "export $PATH=$HOME/.local/bin:$PATH" >> ".$(echo $SHELL | sed -nE "s|.*/(.*)\$|\1|p")rc" + +git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf +~/.fzf/install + +mkdir ~/.aria2c +wget -O ~/.aria2c/aria2-1.36.0.tar.bz2 https://github.com/q3aql/aria2-static-builds/releases/download/v1.36.0/aria2-1.36.0-linux-gnu-64bit-build1.tar.bz2 +tar xvf ~/.aria2c/aria2-1.36.0.tar.bz2 -C ~/.aria2c/ +cp ~/.aria2c/aria2-1.36.0-linux-gnu-64bit-build1/aria2c ~/.local/bin/ +chmod +x ~/.local/bin/aria2c + +git clone https://github.com/pystardust/ani-cli.git ~/.ani-cli +cd ~/.ani-cli && git checkout v4 && cd +cp ~/.ani-cli/ani-cli ~/.local/bin/ +chmod +x ~/.local/bin/ani-cli + +flatpak install io.mpv.Mpv +``` +press enter("A" button on Steam Deck) on questions + +#### Installation in steps: + +##### Install mpv (Flatpak version): + +``` +flatpak install io.mpv.Mpv +``` +press enter("A" button on Steam Deck) on questions + +##### Install [fzf](https://github.com/junegunn/fzf): + +``` +git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf +~/.fzf/install +``` +press enter("A" button on Steam Deck) on questions + +##### Make a ~/.local/bin folder if doesnt exist and add it to $PATH + +``` +[ ! -d ~/.local/bin ] && mkdir ~/.local/bin && echo "export $PATH=$HOME/.local/bin:$PATH" >> ".$(echo $SHELL | sed -nE "s|.*/(.*)\$|\1|p")rc" +``` + +##### Install [aria2](https://github.com/aria2/aria2) (needed for download feature only): + +``` +mkdir ~/.aria2c +wget -O ~/.aria2c/aria2-1.36.0.tar.bz2 https://github.com/q3aql/aria2-static-builds/releases/download/v1.36.0/aria2-1.36.0-linux-gnu-64bit-build1.tar.bz2 +tar xvf ~/.aria2c/aria2-1.36.0.tar.bz2 -C ~/.aria2c/ +cp ~/.aria2c/aria2-1.36.0-linux-gnu-64bit-build1/aria2c ~/.local/bin/ +chmod +x ~/.local/bin/aria2c +``` + +##### Install ani-cli: + +``` +git clone https://github.com/pystardust/ani-cli.git ~/.ani-cli +cd ~/.ani-cli && git checkout v4 && cd - +cp ~/.ani-cli/ani-cli ~/.local/bin/ +chmod +x ~/.local/bin/ani-cli +``` + +##### Optional: add desktop entry: + +``` +echo '[Desktop Entry] +Encoding=UTF-8 +Version=4.0 +Type=Application +Exec=konsole -e ani-cli +Name=ani-cli' > ~/.local/share/applications/ani-cli.desktop +``` +The .desktop entry will allow to start ani-cli in Konsole directly from "Gaming Mode" +In Steam Desktop app: +`Add game` > `Add a non-steam game` > tick a box for `ani-cli` > `Add selected programs` +*Note: Konsole window size bugs out if launched from "Gaming Mode".* +*Note: this is not working the way it should yet.* + ## Uninstall * apt: @@ -253,6 +341,18 @@ pkg remove ani-cli ```sh rm "$PREFIX/bin/ani-cli" ``` +* Steam Deck +``` +rm "~/.local/bin/ani-cli" +rm -rf ~/.ani-cli +``` +optionally: remove dependencies: +``` +rm ~/.local/bin/aria2c +rm -rf "~/.aria2" +rm -rf "~/.fzf" +flatpak uninstall io.mpv.Mpv +``` ## Dependencies @@ -268,6 +368,7 @@ rm "$PREFIX/bin/ani-cli" - ffmpeg - m3u8 Downloader - fzf - User interface + ## Homies * [animdl](https://github.com/justfoolingaround/animdl): Ridiculously efficient, fast and light-weight (supports most sources: animixplay, 9anime...) (Python) From 2307b207e9134d65e22ad8889149c51fa6dbdbbd Mon Sep 17 00:00:00 2001 From: justchokingaround Date: Sun, 22 Jan 2023 18:24:30 +0100 Subject: [PATCH 35/40] feat: if terminal then external menu 0, else 1 Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ani-cli b/ani-cli index d6a822a3a..89dd4d930 100755 --- a/ani-cli +++ b/ani-cli @@ -243,7 +243,7 @@ case "$(uname -a)" in *) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS esac -use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" +[ -t 0 ] && use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" || use_external_menu="${ANI_CLI_EXTERNAL_MENU:-1}" [ "$use_external_menu" = "0" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-${multi_selection_flag:-"-m"}}" [ "$use_external_menu" = "1" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-${multi_selection_flag:-"-multi-select"}}" cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" From f9ee7017ccfe7feefa0a96fa85f52f53c01d4c6c Mon Sep 17 00:00:00 2001 From: zen <71zenith@proton.me> Date: Sun, 22 Jan 2023 23:43:14 +0530 Subject: [PATCH 36/40] feat: add line number to menu Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 75 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/ani-cli b/ani-cli index 89dd4d930..f5cd1e3b3 100755 --- a/ani-cli +++ b/ani-cli @@ -18,16 +18,16 @@ nth() { stdin=$(cat -) [ -z "$stdin" ] && return 1 line_count="$(printf "%s\n" "$stdin" | wc -l)" - [ "$line_count" -eq 1 ] && printf "%s" "$stdin" && return 0 + [ "$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" | sed 's|.*\t||' | launcher "$multi_flag" "$prompt") - [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" || exit 1 + line=$(printf "%s" "$stdin" | cut -f1,3 --output-delimiter " " | launcher "$multi_flag" "$prompt" | cut -d " " -f 2-) + [ -n "$line" ] && printf "%s" "$stdin" | grep -w "${line}$" | cut -f2,3 || exit 1 } die() { - printf "%s\n" "$*" 1>&2 + printf "\33[2K\r\033[1;31m%s\033[0m\n" "$*" >&2 exit 1 } @@ -141,7 +141,6 @@ generate_link() { 4) provider_init 'usercloud' '/Uv-mp4 :/p' ;; # usercloud(mp4)(single) *) provider_init 'gogoanime' '/Luf-mp4 :/p' ;; # gogoanime(m3u8)(multi) esac - # logic yet to implement [ -n "$provider_id" ] && get_links "$provider_id" } @@ -227,6 +226,28 @@ play_episode() { [ "$use_external_menu" = "1" ] && wait } +play() { + start=$(printf "%s" "$ep_no" | sed "s/[^0-9]*\([0-9]*\).*/\1/") + end=$(printf "%s" "$ep_no" | sed "s/[^0-9]*[0-9]*[^0-9]*\([0-9]*\).*/\1/") + [ -z "$start" ] || [ -z "$end" ] && unset start end + line_count=$(printf "%s\n" "$ep_no" | wc -l) + if [ "$line_count" != 1 ] || [ -n "$start" ] || [ -n "$end" ]; then + [ -z "$start" ] && start=$(printf "%s\n" "$ep_no" | tail -n1) + [ -z "$end" ] && end=$(printf "%s\n" "$ep_no" | head -n1) + range=$(printf "%s\n" "$ep_list" | sed '1!G;h;$!d' | 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 +} + + # MAIN # setup @@ -243,9 +264,9 @@ case "$(uname -a)" in *) player_function="${ANI_CLI_PLAYER:-mpv}" ;; # Linux OS esac -[ -t 0 ] && use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" || use_external_menu="${ANI_CLI_EXTERNAL_MENU:-1}" -[ "$use_external_menu" = "0" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-${multi_selection_flag:-"-m"}}" -[ "$use_external_menu" = "1" ] && multi_selection_flag="${ANI_CLI_MULTI_SELECTION:-${multi_selection_flag:-"-multi-select"}}" +use_external_menu="${ANI_CLI_EXTERNAL_MENU:-0}" +[ "$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"}" cache_dir="${ANI_CLI_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/ani-cli}" [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" hist_dir="${ANI_CLI_HIST_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/ani-cli}" @@ -286,7 +307,7 @@ while [ $# -gt 0 ]; do ;; --dub) mode="dub" ;; -U | --update) update_script ;; - *) query="$(printf "%s" "$query $1" | sed "s/^ //;s/ /%20/g")" ;; + *) query="$(printf "%s" "$query $1" | sed "s|^ ||;s| |%20|g")" ;; esac shift done @@ -299,7 +320,7 @@ flatpak*) dep_ch "flatpak" flatpak info io.mpv.Mpv >/dev/null 2>&1 || die "Program \"mpv (flatpak)\" not found. Please install it." ;; -android*) printf "Dependency checking of players on Android is disabled\n" ;; +android*) printf "Checking of players on Android is disabled\n" ;; *) dep_ch "$player_function" ;; esac @@ -309,7 +330,8 @@ 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 histroy!" - result=$(printf "%s" "$anime_list" | nth "Select anime: " | cut -f1) + result=$(printf "%s" "$anime_list" | nl -w 1 | nth "Select anime: " | cut -f1) + [ -z "$result" ] && exit 1 result=$(grep "$result" "$histfile") read -r ep_no id title <<-EOF $result @@ -330,9 +352,7 @@ history) query=$(printf "%s" "$query" | sed "s| |%20|g") anime_list=$(search_anime "$query") [ -z "$anime_list" ] && die "No results found!" - result=$(printf "%s" "$anime_list" | nth "Select anime: ") || exit 1 - # moves the cursor up one line and clears that line - tput cuu1 && tput el + result=$(printf "%s" "$anime_list" | nl -w 1 | nth "Select anime: ") || exit 1 title=$(printf "%s" "$result" | cut -f2) allanime_title="$(printf "%s" "$title" | cut -d'(' -f1 | tr -d '[:punct:]' | tr 'A-Z ' 'a-z-')" id=$(printf "%s" "$result" | cut -f1) @@ -342,36 +362,19 @@ history) ;; esac +# moves the cursor up one line and clears that line +tput cuu1 && tput el + # playback & loop -play() { - start=$(printf "%s" "$ep_no" | sed "s/[^0-9]*\([0-9]*\).*/\1/") - end=$(printf "%s" "$ep_no" | sed "s/[^0-9]*[0-9]*[^0-9]*\([0-9]*\).*/\1/") - [ -z "$start" ] || [ -z "$end" ] && unset start end - line_count=$(printf "%s\n" "$ep_no" | wc -l) - if [ "$line_count" != 1 ] || [ -n "$start" ] || [ -n "$end" ]; then - [ -z "$start" ] && start=$(printf "%s\n" "$ep_no" | tail -n1) - [ -z "$end" ] && end=$(printf "%s\n" "$ep_no" | head -n1) - range=$(printf "%s\n" "$ep_list" | sed '1!G;h;$!d' | sed -nE "/^${start}\$/,/^${end}\$/p") - [ -z "$range" ] && die "Invalid range!" - for i in $range; do - ep_no=$i - printf "\33[2K\r\033[1;34mPlaying episode %s...\033[0m\n" "$ep_no" - play_episode - tput clear - done - else - play_episode - fi -} play -[ "$player_function" = "download" ] && exit 0 +[ "$player_function" = "download" ] || [ "$player_function" = "debug" ] && exit 0 while cmd=$(printf "quit\nselect\nprevious\nreplay\nnext" | nth "Playing episode $ep_no of $title... "); do case "$cmd" in previous) ep_no=$(printf "%s" "$ep_list" | sed -n "/^${ep_no}$/{n;p;}") 2>/dev/null ;; replay) ;; next) 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: ") ;; + select) ep_no=$(printf "%s" "$ep_list" | nth "Select episode: " "$multi_selection_flag") ;; *) exit 0 ;; esac [ -z "$ep_no" ] && die "Out of range" From 4faa1c80c83dfeac61f72622a00f0a25dc7d9563 Mon Sep 17 00:00:00 2001 From: port19 Date: Sun, 22 Jan 2023 12:08:56 +0100 Subject: [PATCH 37/40] docs: readme update + docs: remove old deps and add syntax to code blocks + docs: acknowledging history disappearence + docs: missing fzf in brew installation + docs: windows instructions for v4 + docs: termux instructions for v4 Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- README.md | 109 +++++++++++++++++++++++++----------------------------- 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 20e085ee4..10cbc5add 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@

-
@@ -20,7 +19,7 @@

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

@@ -32,7 +31,7 @@ https://user-images.githubusercontent.com/44473782/160729779-41fe207c-b5aa-4fed- ## Table of Contents - [Fixing errors](#Fixing-errors) -- [New in v3](#New-in-v3) +- [New in v4](#New-in-v4) - [Install](#Install) - [Linux](#Linux) - [Debian](#Debian) @@ -43,7 +42,7 @@ https://user-images.githubusercontent.com/44473782/160729779-41fe207c-b5aa-4fed- - [MacOS](#MacOS) - [Windows](#Windows) - [Android](#Android) - - [Steam Deck](#steam-deck) + - [Steam Deck](#Steam-deck) - [Uninstall](#Uninstall) - [Dependencies](#Dependencies) - [Homies](#Homies) @@ -56,6 +55,14 @@ If you encounter "Video url not found" or any breaking issue, then make sure you `sudo ani-cli -U` to update on Linux, Mac and Android. On Windows, run gitbash as administrator then there type `ani-cli -U`. If after this the issue persists then open an issue. +History has been reworked and relocated. We're working on a transition script, please be patient. Old history can be viewed with `less ${XDG_CACHE_HOME:-$HOME/.cache}/ani-hsts` + +## New in v4 + +V4 was a complete rewrite, which is why it took so long. + +The user interface is now powered by [fzf](https://github.com/junegunn/fzf), tho it may be changed to [gum](https://github.com/charmbracelet/gum) in an upcoming version. + ## Install #### Users of V3.2 or the v3.2.x series should uninstall before upgrading @@ -63,7 +70,7 @@ Otherwise you're likely to see an error like the following: ` "/usr/bin/ani-cli: ### Native packages -[![Packaging status](https://repology.org/badge/vertical-allrepos/ani-cli.svg?minversion=3.0)](https://repology.org/project/ani-cli/versions) +[![Packaging status](https://repology.org/badge/vertical-allrepos/ani-cli.svg?minversion=4.0)](https://repology.org/project/ani-cli/versions) *Native packages have a more robust update cycle, but sometimes they are slow to upgrade. If the one for your platform is up-to-date we suggest going with it.* @@ -71,7 +78,7 @@ Otherwise you're likely to see an error like the following: ` "/usr/bin/ani-cli: #### Debian -``` +```sh wget -qO- https://Wiener234.github.io/ani-cli-ppa/KEY.gpg | sudo tee /etc/apt/trusted.gpg.d/ani-cli.asc wget -qO- https://Wiener234.github.io/ani-cli-ppa/ani-cli-debian.list | sudo tee /etc/apt/sources.list.d/ani-cli-debian.list sudo apt update @@ -96,7 +103,7 @@ Build and install from the AUR: ```sh yay -S ani-cli ``` -Also consider ani-cli-git +Also consider `ani-cli-git` #### OpenSuse Tumbleweed and Leap @@ -111,7 +118,7 @@ zypper install ani-cli ``` You'll get a warning about `Signature verification failed [4-Signatures public key is not available]` but this can be ignored from the prompt. -*Note: package is noarch, so any architecture should work, even though the repo is labled x86-64* +*Note: package is noarch, so any architecture should work, even though the repo is labelled x86-64* #### Installing from source @@ -119,12 +126,10 @@ Install dependencies [(See below)](#Dependencies) ```sh sudo rm -rf "/usr/local/share/ani-cli" "/usr/local/bin/ani-cli" "/usr/local/bin/UI" /usr/local/bin/player_* #If some of these aren't found, it's not a problem -git clone "https://github.com/pystardust/ani-cli.git" && cd ./ani-cli -sudo cp ./ani-cli /usr/local/bin -cd .. && rm -rf "./ani-cli" +git clone "https://github.com/pystardust/ani-cli.git" +sudo cp ani-cli/ani-cli /usr/local/bin +rm -rf ani-cli ``` -*Also note that mpv installed through flatpak is not compatible* - ### MacOS @@ -142,21 +147,20 @@ cd .. && rm -rf ./ani-cli *To install (with Homebrew) the dependencies required on Mac OS, you can run:* ```sh -brew install curl grep axel openssl@1.1 ffmpeg git && \ +brew install wget grep aria2 ffmpeg git fzf && \ brew install --cask iina ``` *Why iina and not mpv? Drop-in replacement for mpv for MacOS. Integrates well with OSX UI. Excellent support for M1. Open Source.* ### Windows -*Make sure git bash is installed [(Install)](https://git-scm.com/download/win)* +*ani-cli needs a posix shell and the current way is git bash. Unfortunately fzf can't run in git bash's default terminal. The solution is to use git bash in windows terminal* -*Note that the installation instruction below must be done inside **Git Bash**, not in Command Prompt or Powershell* +First, you'll need windows terminal preview. [(Install)](https://apps.microsoft.com/store/detail/windows-terminal-preview/9N8G5RFZ9XK3?hl=de-at&gl=at&rtc=1) -mpv is not added to $PATH automatically when installed and thus the script is unable to use it. You either have to do this manually, or install it via scoop (recommended): -```sh -scoop install mpv -``` +Then make sure git bash is installed. [(Install)](https://git-scm.com/download/win) It needs to be added to windows terminal [(Instructions)](https://stackoverflow.com/questions/56839307/adding-git-bash-to-the-new-windows-terminal) + +The following steps and ani-cli need to be run from git bash in windows terminal. #### Scoop bucket @@ -168,12 +172,14 @@ scoop install ani-cli #### From source ```sh rm -rf "/usr/local/share/ani-cli" "/usr/local/bin/ani-cli" "/usr/local/bin/UI" /usr/local/bin/player_* #If some of these aren't found, it's not a problem -git clone "https://github.com/pystardust/ani-cli.git" && cd ./ani-cli -cp ./ani-cli /usr/bin -cd .. && rm -rf ./ani-cli +git clone "https://github.com/pystardust/ani-cli.git" +cp ani-cli/ani-cli /usr/bin +rm -rf /ani-cli ``` -*Run ani-cli in Git Bash (Running it in cmd or powershell may or may not work)* +#### Dependencies + +All dependencies can be installed with scoop (from the extras bucket), however some users experienced that installed programs aren't always added to the path. If this happens installing from winget instead usually works. ### Android @@ -191,18 +197,12 @@ pkg install ani-cli ```sh pkg up -y rm -rf "$PREFIX/share/ani-cli" "$PREFIX/bin/ani-cli" "$PREFIX/bin/UI" "$PREFIX"/local/bin/player_* #If some of these aren't found, it's not a problem -git clone "https://github.com/pystardust/ani-cli.git" && cd ./ani-cli -cp ./ani-cli "$PREFIX"/bin -cd .. && rm -rf ./ani-cli +git clone "https://github.com/pystardust/ani-cli.git" +cp ani-cli/ani-cli "$PREFIX"/bin +rm -rf /ani-cli ``` -Note : Vlc Android now works too ;) - -You need to add any referrer in mpv by opening mpv [(playstore version)](https://play.google.com/store/apps/details?id=is.xyz.mpv), going into Settings -> Advanced -> Edit mpv.conf and adding (for example): - -``` -referrer="https://animixplay.to/" -``` +For players you can use the apk (playstore/fdroid) versions of mpv and vlc. Note that these cannot be checked from termux so a warning is generated when checking dependencies. ### Steam Deck @@ -212,7 +212,7 @@ referrer="https://animixplay.to/" * Open `Konsole` (Steam Deck Icon in bottom left corner > System > Konsole) * Copy the script, paste it in the CLI and press Enter("A" button on Steam Deck) -``` +```sh [ ! -d ~/.local/bin ] && mkdir ~/.local/bin && echo "export $PATH=$HOME/.local/bin:$PATH" >> ".$(echo $SHELL | sed -nE "s|.*/(.*)\$|\1|p")rc" git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf @@ -225,9 +225,7 @@ cp ~/.aria2c/aria2-1.36.0-linux-gnu-64bit-build1/aria2c ~/.local/bin/ chmod +x ~/.local/bin/aria2c git clone https://github.com/pystardust/ani-cli.git ~/.ani-cli -cd ~/.ani-cli && git checkout v4 && cd cp ~/.ani-cli/ani-cli ~/.local/bin/ -chmod +x ~/.local/bin/ani-cli flatpak install io.mpv.Mpv ``` @@ -237,28 +235,28 @@ press enter("A" button on Steam Deck) on questions ##### Install mpv (Flatpak version): -``` +```sh flatpak install io.mpv.Mpv ``` press enter("A" button on Steam Deck) on questions ##### Install [fzf](https://github.com/junegunn/fzf): -``` +```sh git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf ~/.fzf/install ``` press enter("A" button on Steam Deck) on questions -##### Make a ~/.local/bin folder if doesnt exist and add it to $PATH +##### Make a ~/.local/bin folder if doesn't exist and add it to $PATH -``` +```sh [ ! -d ~/.local/bin ] && mkdir ~/.local/bin && echo "export $PATH=$HOME/.local/bin:$PATH" >> ".$(echo $SHELL | sed -nE "s|.*/(.*)\$|\1|p")rc" ``` ##### Install [aria2](https://github.com/aria2/aria2) (needed for download feature only): -``` +```sh mkdir ~/.aria2c wget -O ~/.aria2c/aria2-1.36.0.tar.bz2 https://github.com/q3aql/aria2-static-builds/releases/download/v1.36.0/aria2-1.36.0-linux-gnu-64bit-build1.tar.bz2 tar xvf ~/.aria2c/aria2-1.36.0.tar.bz2 -C ~/.aria2c/ @@ -268,11 +266,9 @@ chmod +x ~/.local/bin/aria2c ##### Install ani-cli: -``` +```sh git clone https://github.com/pystardust/ani-cli.git ~/.ani-cli -cd ~/.ani-cli && git checkout v4 && cd - cp ~/.ani-cli/ani-cli ~/.local/bin/ -chmod +x ~/.local/bin/ani-cli ``` ##### Optional: add desktop entry: @@ -294,13 +290,13 @@ In Steam Desktop app: ## Uninstall * apt: -``` +```sh sudo apt remove ani-cli -# to remove the repository from apt: +# to remove the repository from apt sudo rm -f /etc/apt/trusted.gpg.d/ani-cli.asc /etc/apt/sources.list.d/ani-cli-debian.list ``` * dnf: -``` +```sh sudo dnf remove ani-cli # for ani-cli # disable the repo in dnf dnf copr disable derisis13/ani-cli @@ -313,7 +309,7 @@ zypper removerepo ani-cli ``` You might want to remove `packman-essentials` if you don't need it otherwise * AUR: -``` +```sh yay -R ani-cli ``` * Scoop: @@ -334,7 +330,7 @@ In **Git Bash** run (as administrator): rm "/usr/bin/ani-cli" ``` * Termux package -``` +```sh pkg remove ani-cli ``` * Android: @@ -342,12 +338,12 @@ pkg remove ani-cli rm "$PREFIX/bin/ani-cli" ``` * Steam Deck -``` +```sh rm "~/.local/bin/ani-cli" rm -rf ~/.ani-cli ``` optionally: remove dependencies: -``` +```sh rm ~/.local/bin/aria2c rm -rf "~/.aria2" rm -rf "~/.fzf" @@ -358,20 +354,17 @@ flatpak uninstall io.mpv.Mpv - grep - sed -- awk -- curl - wget -- openssl - mpv - Video Player - iina - mpv replacement for MacOS - aria2c - Download manager - ffmpeg - m3u8 Downloader - fzf - User interface +## Homies -## Homies - -* [animdl](https://github.com/justfoolingaround/animdl): Ridiculously efficient, fast and light-weight (supports most sources: animixplay, 9anime...) (Python) +* [animdl](https://github.com/justfoolingaround/animdl): Ridiculously efficient, fast and light-weight (supports most sources: allanime, zoro ... (Python) +* [jerry](https://github.com/justchokingaround/jerry): stream anime with anilist tracking and syncing, with discord presence (Shell) * [anime-helper-shell](https://github.com/Atreyagaurav/anime-helper-shell): A python shell for searching, watching, and downloading anime (Python) * [anipy-cli](https://github.com/sdaqo/anipy-cli): ani-cli rewritten in python (Python) * [dra-cla](https://github.com/CoolnsX/dra-cla): ani-cli equivalent for korean dramas (Shell) From 2ddcad8d0ad0987e31c8423904dfad28c6b86ebd Mon Sep 17 00:00:00 2001 From: Derisis13 Date: Mon, 23 Jan 2023 01:09:48 +0100 Subject: [PATCH 38/40] docs: manpage + docs: remove v3.4 stuff + docs: dmenu -> rofi dmenu (dmenu doesn't support multi) + docs: remove "new in v4" Co-authored-by: archeite <121004047+archeite@users.noreply.github.com> Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- .assets/ani-cli-spec.png | Bin 54394 -> 0 bytes .github/CODEOWNERS | 1 + CONTRIBUTING.md | 4 -- README.md | 7 --- ani-cli.1 | 94 +++++++++++++++++++++++---------------- 5 files changed, 57 insertions(+), 49 deletions(-) delete mode 100644 .assets/ani-cli-spec.png diff --git a/.assets/ani-cli-spec.png b/.assets/ani-cli-spec.png deleted file mode 100644 index 2856c4dfe59a7916590bd296ea39bd657debd0ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54394 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 WkD>X8dg;WU59~A4dZ=L&{{H}>AUt>g diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4b898a249..51eff285e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,3 +2,4 @@ /.assets/ @justchokingaround README.md @justchokingaround hist_transition.sh @Derisis13 +ani-cli.1 @Derisis13 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5470cfbf3..7eeb31147 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,3 @@ - Take part in troubleshooting and testing - Star the repo - Follow the maintainers - -## Architecture - -![spec](https://github.com/pystardust/ani-cli/blob/spec/.assets/ani-cli-spec.png) diff --git a/README.md b/README.md index 10cbc5add..b5c94748e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ https://user-images.githubusercontent.com/44473782/160729779-41fe207c-b5aa-4fed- ## Table of Contents - [Fixing errors](#Fixing-errors) -- [New in v4](#New-in-v4) - [Install](#Install) - [Linux](#Linux) - [Debian](#Debian) @@ -57,12 +56,6 @@ If after this the issue persists then open an issue. History has been reworked and relocated. We're working on a transition script, please be patient. Old history can be viewed with `less ${XDG_CACHE_HOME:-$HOME/.cache}/ani-hsts` -## New in v4 - -V4 was a complete rewrite, which is why it took so long. - -The user interface is now powered by [fzf](https://github.com/junegunn/fzf), tho it may be changed to [gum](https://github.com/charmbracelet/gum) in an upcoming version. - ## Install #### Users of V3.2 or the v3.2.x series should uninstall before upgrading diff --git a/ani-cli.1 b/ani-cli.1 index f05e5e80c..44152cac9 100644 --- a/ani-cli.1 +++ b/ani-cli.1 @@ -1,4 +1,4 @@ -.TH "ANI-CLI" "1" "December 2022" "ani-cli" "User Commands" +.TH "ANI-CLI" "1" "January 2023" "ani-cli" "User Commands" .SH NAME ani-cli \- watch anime from the commandline .SH SYNOPSIS @@ -9,63 +9,81 @@ A shell script to browse and search anime from the command-line. .PD 0 .P .PD -This tool scrapes the site animixplay. +This tool scrapes the site allanime. .PD 0 .P .PD -\f[B]ani-cli\f[R] without options defaults to the mpv media player. +\f[B]ani-cli\f[R] without options defaults to iina on macOS, flatpak mpv on Steamdeck, mpv apk on android and mpv media player everywhere else. .SH OPTIONS .TP -\fB\-a\fR \fI\,\/\fR -Specify the episode number to watch. +\fB\-e | --episode | -r | --range\fR \fI\,\/\fR +Specify the episode numbers to watch. If range is specified it should be quoted or separated by a non-numeric character (eg. -). .TP -\fB\-c\fR -continue watching anime from history. +\fB\-c | --continue\fR +Continue watching anime from history. .TP -\fB\-d\fR -download episode. +\fB\-d | --download\fR +Download episode. .TP -\fB\-D\fR -delete history. +\fB\-h | --help\fR +Show summary of options. .TP -\fB\-f\fR -use fzf for anime selection. +\fB\-q | --quality\fR \fI\,\/\fR +Set the video quality. Default quality is best. .TP -\fB\-h\fR -Show summary of options. +\fB\-s | --syncplay\fR +Watch anime together with friends, using Syncplay (works with mpv only). +.TP +\fB\-U | --update\fR +Fetch update from github. .TP -\fB\-p\fR \fI\,DIRECTORY\/\fR -download episode to the specified DIRECTORY. +\fB\-v | --vlc\fR +Use VLC as the media player. .TP -\fB\-q\fR \fI\,\/\fR -set the video quality. +\fB\-V | --version\fR +Print version number and exit. .TP -\fB\-r\fR -select provider to scrape first. +\fB\--dub\fR +Play the dubbed version. Without this flag, it'll always play the subbed version. +.PP +.SH +ENVIRONMENT VARIABLES +.PP +ani-cli v4 uses environment variables to control unstable/untested and niche features in addition to everything that has an option. Command-line options take precedence over env vars. Note that these are all subject to change. .TP -\fB\-s\fR -watch anime together with friends, using Syncplay (works with mpv only). +\fBANI_CLI_MODE\fR +Controls the scraped media's mode, valid options are sub or dub. Default is sub. .TP -\fB\-U\fR -fetch update from github. +\fBANI_CLI_MEDIA_LOCALE\fR +Controls the scraped media's locale, check allanime for valid options. Default is en-US. .TP -\fB\-v\fR -use VLC as the media player. +\fBANI_CLI_DOWNLOAD_DIR\fR +Controls the directory where files are downloaded. Default is the current dir. .TP -\fB\-V\fR -print version number and exit. +\fBANI_CLI_QUALITY\fR +Controls the scraped media's quality, check allanime for valid options or set to worst/best. Default is best. .TP -\fB\-x\fR -print all video links from all providers to stdout (for debugging -purpose). -.SH EPISODE SELECTION +\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), flatpak_mpv (for flatpak) or any player that can play urls. For defaults see working without arguments. +.TP +\fBANI_CLI_EXTERNAL_MENU\fR +Controls the frontend of ani-cli. Can be 0 (uses fzf) or 1 (uses rofi dmenu). Default is 0. +.TP +\fBANI_CLI_MULTI_SELECTION\fR +Controls the multi flag for the chosen frontend. Default is -m for fzf and --multi-select for rofi dmenu. +.TP +\fBANI_CLI_CACHE_DIR\fR +Controls the directory ani-cli uses for caching results. A /ani-cli subfolder is created there for the cache files if doesn't exists. Default is $XDG_CACHE_HOME if set, $HOME/.cache/ if not. +.TP +\fBANI_CLI_HIST_DIR\fR +Controls the directory ani-cli uses for storing history. A /ani-cli subfolder is created there for the histfile if doesn't exists. Default is $XDG_STATE_HOME if set, $HOME/.local/state if not. +.TP +\fBANI_CLI_DEFAULT_SOURCE\fR +Controls the default source. Valid is history (equivalent to -c), everything else means search. Default is search. .PP -Episode selection: Multiple episodes can be chosen given a range Choose -episode [1-13]: 1 6 This would choose episodes 1 2 3 4 5 6 To select the -last episode use -1 +.SH EPISODE SELECTION .PP -When selecting non-interactively, the first result will be selected, if -anime is passed +Multiple episodes can be chosen using fzf (or alternative frontend's) multi-selection mode. For this refer to their instructions. .SH BUGS .PP Use the GitHub issue tracker: From b26e007d93c900d6057f2ae3a517531b07e17a50 Mon Sep 17 00:00:00 2001 From: port19 Date: Mon, 23 Jan 2023 14:34:15 +0100 Subject: [PATCH 39/40] docs: expand on PR template Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- .github/PULL_REQUEST_TEMPLATE.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4ca5f7a4f..ca63c00e3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,16 +14,21 @@ - [ ] any anime playing - [ ] bumped version +--- - [ ] next, prev and replay work -- [ ] quality works -- [ ] downloads work -- [ ] quality works with downloads -- [ ] select episode -a works -- [ ] vlc -v and syncplay -s work -- [ ] autoplay, aka range selection, works -- [ ] history and -c work -- [ ] sub and dub both work +- [ ] `-c` history and continue work +- [ ] `-d` downloads work +- [ ] `-s` syncplay works +- [ ] `-q` quality works +- [ ] `-v` vlc works +- [ ] `-e` select episode works +- [ ] `-r` range selection works +- [ ] `--dub` both work - [ ] all providers return links (not necessarily on a single anime, use debug mode to confirm) +--- +- [ ] `-h` help info is up to date +- [ ] Readme is up to date +- [ ] Man page is up to date ## Additional Testcases From a669ec4ecbf73516ccf3f5e1716e2d67070bebdb Mon Sep 17 00:00:00 2001 From: port19 Date: Mon, 23 Jan 2023 13:09:00 +0100 Subject: [PATCH 40/40] chore: v4.0 -> v4.0.0 The version variable inside the script should always be major.minor.patch major.minor releases are reserved for native packages and tags/releases Co-authored-by: port19 Co-authored-by: Derisis13 Co-authored-by: CoolnsX Co-authored-by: chokerman <44473782+justchokingaround@users.noreply.github.com> Co-authored-by: zenith71 <71zenith@protonmail.com> --- ani-cli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ani-cli b/ani-cli index f5cd1e3b3..f8e073ca6 100755 --- a/ani-cli +++ b/ani-cli @@ -1,6 +1,6 @@ #!/bin/sh -version_number="4.0" +version_number="4.0.0" # UI