ani-cli-npm 2.1.3 → 2.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +2 -0
- package/bin/Anime.js +46 -30
- package/bin/IO/help.js +59 -0
- package/bin/IO/input.js +89 -0
- package/bin/cover_manager.js +3 -0
- package/bin/download.js +2 -2
- package/bin/file_managment/change_config.js +3 -2
- package/bin/file_managment/load_config.js +2 -1
- package/bin/index.js +8 -3
- package/bin/url_genoration/generate_link.js +47 -0
- package/bin/url_genoration/search_anime.js +39 -0
- package/package.json +1 -1
- package/src/Anime.ts +427 -0
- package/src/IO/help.ts +64 -0
- package/src/IO/input.ts +92 -0
- package/src/core_utils/curl.ts +32 -0
- package/src/core_utils/interfaces.ts +25 -0
- package/src/core_utils/libs.ts +13 -0
- package/src/core_utils/regex.ts +5 -0
- package/src/cover_manager.ts +9 -0
- package/src/download.ts +35 -0
- package/src/file_managment/cache.ts +58 -0
- package/src/file_managment/change_config.ts +126 -0
- package/src/file_managment/load_config.ts +79 -0
- package/src/index.ts +143 -0
- package/src/url_genoration/generate_link.ts +53 -0
- package/src/url_genoration/search_anime.ts +41 -0
- package/src/video_quality.ts +6 -0
- package/temp.sh +676 -0
    
        package/temp.sh
    ADDED
    
    | @@ -0,0 +1,676 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            #!/bin/sh
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            #---
         | 
| 5 | 
            +
            #dependencies:
         | 
| 6 | 
            +
            #  curl.se: ^7.8
         | 
| 7 | 
            +
            #  openssl.org: ^1.1
         | 
| 8 | 
            +
            #  ffmpeg.org: ^5.1
         | 
| 9 | 
            +
            #---
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # License preamble at the end of the file
         | 
| 12 | 
            +
            # Version number
         | 
| 13 | 
            +
            VERSION="3.4.7"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
             | 
| 16 | 
            +
            #######################
         | 
| 17 | 
            +
            # AUXILIARY FUNCTIONS #
         | 
| 18 | 
            +
            #######################
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            help_text () {
         | 
| 21 | 
            +
            	while read -r line; do
         | 
| 22 | 
            +
            		printf "%s\n" "$line"
         | 
| 23 | 
            +
            	done <<-EOF
         | 
| 24 | 
            +
            	Usage:
         | 
| 25 | 
            +
            	  ${0##*/} [-f] [-s] [-v] [-x] [-q <quality>] [-a <episode>] [-d | -p <download_dir>] [<query>] [-r <provider_no.>]
         | 
| 26 | 
            +
            	  ${0##*/} [-f] [-s] [-v] [-x] [-q <quality>] -c
         | 
| 27 | 
            +
            	  ${0##*/} -h | -D | -U | -V
         | 
| 28 | 
            +
            	Options:
         | 
| 29 | 
            +
            	  -v use VLC as the media player
         | 
| 30 | 
            +
            	  -q set video quality (best|worst|360|480|720|1080)
         | 
| 31 | 
            +
            	  -s watch anime together with friends, using Syncplay (works with mpv only)
         | 
| 32 | 
            +
            	  -f use fzf for anime selection
         | 
| 33 | 
            +
            	  -a specify episode to watch
         | 
| 34 | 
            +
            	  -d download episode
         | 
| 35 | 
            +
            	  -p download episode to specified directory
         | 
| 36 | 
            +
            	  -c continue watching anime from history
         | 
| 37 | 
            +
            	  -h show helptext
         | 
| 38 | 
            +
            	  -D delete history
         | 
| 39 | 
            +
            	  -U fetch update from github
         | 
| 40 | 
            +
            	  -V print version number and exit
         | 
| 41 | 
            +
            	  -r select provider to scrape first [1-3]
         | 
| 42 | 
            +
            	  -x print all video links from all providers to stdout (for debugging purpose)
         | 
| 43 | 
            +
            	Episode selection:
         | 
| 44 | 
            +
            	  Multiple episodes can be chosen given a range
         | 
| 45 | 
            +
            	    Choose episode [1-13]: 1 6
         | 
| 46 | 
            +
            	    This would choose episodes 1 2 3 4 5 6
         | 
| 47 | 
            +
            		To select the last episode use -1
         | 
| 48 | 
            +
            	  When selecting non-interactively, the first result will be
         | 
| 49 | 
            +
            	  selected, if anime is passed
         | 
| 50 | 
            +
            EOF
         | 
| 51 | 
            +
            }
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            retry () {
         | 
| 54 | 
            +
            	err "$*"
         | 
| 55 | 
            +
            	prompt
         | 
| 56 | 
            +
            }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            # display an error message to stderr (in red)
         | 
| 59 | 
            +
            err () {
         | 
| 60 | 
            +
            	printf "\33[2K\r\033[1;31m%s\033[0m\n" "$*" >&2
         | 
| 61 | 
            +
            }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            #display error message and exit
         | 
| 64 | 
            +
            die () {
         | 
| 65 | 
            +
            	err "$*"
         | 
| 66 | 
            +
            	exit 1
         | 
| 67 | 
            +
            }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            # display an informational message (first argument in green, second in magenta)
         | 
| 70 | 
            +
            inf () {
         | 
| 71 | 
            +
            	printf "\33[2K\r\033[1;35m%s \033[1;35m%s\033[0m\n" "$1" "$2"
         | 
| 72 | 
            +
            }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            progress() {
         | 
| 75 | 
            +
            	printf "\33[2K\r\033[1;34m%s\033[0m" "$1"
         | 
| 76 | 
            +
            }
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            debug () {
         | 
| 79 | 
            +
            	printf "\n\033[1;32mReferrer :\033[0m %s\n\033[1;32mlinks >>\n\033[0m%s\n" "$1" "$2"
         | 
| 80 | 
            +
            }
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            # 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
         | 
| 83 | 
            +
            prompt () {
         | 
| 84 | 
            +
            	[ -n "$*" ] && printf "\33[2K\r\033[1;35m%s\n" "$*"
         | 
| 85 | 
            +
            	printf "\033[1;35m> \033[0m"
         | 
| 86 | 
            +
            	read -r REPLY REPLY2
         | 
| 87 | 
            +
            }
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            selection_menu() {
         | 
| 90 | 
            +
            	menu_line_parity=0
         | 
| 91 | 
            +
            	while read -r option line; do
         | 
| 92 | 
            +
            		if [ "$option" = "q" ]; then
         | 
| 93 | 
            +
            			printf "\033[1;31m(\033[1;31m%s\033[1;31m) \033[1;31m%s\033[0m\n" "$option" "$line"
         | 
| 94 | 
            +
            		else
         | 
| 95 | 
            +
            			if [ "$menu_line_parity" -eq 0 ]; then
         | 
| 96 | 
            +
            				printf "\033[1;33m(\033[1;33m%s\033[1;33m) \033[1;33m%s\033[0m\n" "$option" "$line"
         | 
| 97 | 
            +
            				menu_line_parity=1
         | 
| 98 | 
            +
            			else
         | 
| 99 | 
            +
            				printf "\033[1;36m(\033[1;36m%s\033[1;36m) \033[1;36m%s\033[0m\n" "$option" "$line"
         | 
| 100 | 
            +
            				menu_line_parity=0
         | 
| 101 | 
            +
            			fi
         | 
| 102 | 
            +
            		fi
         | 
| 103 | 
            +
            	done <<-EOF
         | 
| 104 | 
            +
            	$*
         | 
| 105 | 
            +
            	EOF
         | 
| 106 | 
            +
            	prompt
         | 
| 107 | 
            +
            }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            selection_menu_fzf() {
         | 
| 110 | 
            +
            	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
         | 
| 111 | 
            +
            }
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            version_text () {
         | 
| 114 | 
            +
            	inf "Version: $VERSION"
         | 
| 115 | 
            +
            }
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            # get the newest version of this script from github and replace it
         | 
| 118 | 
            +
            update_script () {
         | 
| 119 | 
            +
            	update="$(curl -A "$agent" -s "https://raw.githubusercontent.com/pystardust/ani-cli/master/ani-cli")" || die "Connection error"
         | 
| 120 | 
            +
            	update="$(printf '%s\n' "$update" | diff -u "$0" -)"
         | 
| 121 | 
            +
            	if [ -z "$update" ]; then
         | 
| 122 | 
            +
            		inf "Script is up to date :)"
         | 
| 123 | 
            +
            	else
         | 
| 124 | 
            +
            		if printf '%s\n' "$update" | patch "$0" - ; then
         | 
| 125 | 
            +
            			inf "Script has been updated"
         | 
| 126 | 
            +
            		else
         | 
| 127 | 
            +
            			die "Can't update for some reason!"
         | 
| 128 | 
            +
            		fi
         | 
| 129 | 
            +
            	fi
         | 
| 130 | 
            +
            	exit 0
         | 
| 131 | 
            +
            }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
            # checks if dependencies are present
         | 
| 134 | 
            +
            dep_ch () {
         | 
| 135 | 
            +
            	for dep; do
         | 
| 136 | 
            +
            		if ! command -v "$dep" >/dev/null ; then
         | 
| 137 | 
            +
            			die "Program \"$dep\" not found. Please install it."
         | 
| 138 | 
            +
            		fi
         | 
| 139 | 
            +
            	done
         | 
| 140 | 
            +
            }
         | 
| 141 | 
            +
             | 
| 142 | 
            +
            download () {
         | 
| 143 | 
            +
            	case $2 in
         | 
| 144 | 
            +
            		*m3u8*)
         | 
| 145 | 
            +
            			ffmpeg -loglevel error -stats -referer "$1" -i "$2" -c copy "$download_dir/$3.mp4" ;;
         | 
| 146 | 
            +
            		*)	
         | 
| 147 | 
            +
            			axel -a -k -n 10 --header=Referer:"$1" "$2" -o "$download_dir/$3.mp4" ;;
         | 
| 148 | 
            +
            	esac
         | 
| 149 | 
            +
            }
         | 
| 150 | 
            +
             | 
| 151 | 
            +
            #############
         | 
| 152 | 
            +
            # SEARCHING #
         | 
| 153 | 
            +
            #############
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            # gets anime names along with its id for search term
         | 
| 156 | 
            +
            search_anime () {
         | 
| 157 | 
            +
            	search=$(printf '%s' "$1" | tr ' ' '-' )
         | 
| 158 | 
            +
            	curl -s "https://gogoanime.dk//search.html?keyword=$search" -L |
         | 
| 159 | 
            +
            		sed -nE "s_^[[:space:]]*<a href=\"/category/([^\"]*)\" title.*\">\$_\1_p"
         | 
| 160 | 
            +
            }
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            #fetches all the episodes embed links in an anime from gogoanime server
         | 
| 163 | 
            +
            episode_list () {
         | 
| 164 | 
            +
            	select_ep_result=$(curl -A "$agent" -s "$base_url/v1/$1" | sed -nE "s_.*epslistplace.*>(.*)</div>_\1_p" | tr "," "\n" | sed -e '/extra/d' -e '/PV/d' | sed -nE 's_".*":"(.*)".*_\1_p')
         | 
| 165 | 
            +
            	first_ep_number=1
         | 
| 166 | 
            +
            	[ -z "$select_ep_result" ] && last_ep_number=0 || last_ep_number=$(printf "%s\n" "$select_ep_result" | wc -l)
         | 
| 167 | 
            +
            }
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            process_hist_entry () {
         | 
| 170 | 
            +
            	temp_anime_id=$(printf "%s" "$anime_id" | sed 's/\t[0-9]*.$//')
         | 
| 171 | 
            +
            	current_ep=$(printf "%s" "$anime_id" | sed "s/$temp_anime_id\t//g")
         | 
| 172 | 
            +
            	episode_list "$temp_anime_id"
         | 
| 173 | 
            +
            	latest_ep=$last_ep_number
         | 
| 174 | 
            +
            	if [ -n "$latest_ep" ] && [ "$latest_ep" -ge "$current_ep" ]; then
         | 
| 175 | 
            +
            		printf "%s : %s / %s\n" "$temp_anime_id" "$current_ep" "$latest_ep"
         | 
| 176 | 
            +
            	fi
         | 
| 177 | 
            +
            }
         | 
| 178 | 
            +
             | 
| 179 | 
            +
            # compares history with gogoplay, only shows unfinished anime
         | 
| 180 | 
            +
            search_history () {
         | 
| 181 | 
            +
            	[ ! -s "$logfile" ] && die "History is empty"
         | 
| 182 | 
            +
            	progress "Processing $scrape.."
         | 
| 183 | 
            +
            	search_results=$(while read -r anime_id; do process_hist_entry & done < "$logfile"; wait)
         | 
| 184 | 
            +
            	[ -z "$search_results" ] && die "No unwatched episodes"
         | 
| 185 | 
            +
            	one_hist=$(printf '%s\n' "$search_results" | grep -e "$" -c)
         | 
| 186 | 
            +
            	[ "$one_hist" = 1 ] && select_first=1
         | 
| 187 | 
            +
            	anime_selection "$search_results"
         | 
| 188 | 
            +
            	ep_choice_start=$(sed -n -E "s/${selection_id}\t(.*)/\1/p" "$logfile")
         | 
| 189 | 
            +
            }
         | 
| 190 | 
            +
             | 
| 191 | 
            +
            ##################
         | 
| 192 | 
            +
            # URL PROCESSING #
         | 
| 193 | 
            +
            ##################
         | 
| 194 | 
            +
             | 
| 195 | 
            +
            generate_link() {
         | 
| 196 | 
            +
            	case $1 in
         | 
| 197 | 
            +
            		2)
         | 
| 198 | 
            +
            			provider_name='Xstreamcdn'
         | 
| 199 | 
            +
            			progress "Fetching $provider_name links.."
         | 
| 200 | 
            +
            			fb_id=$(printf "%s" "$resp" | sed -n "s_.*fembed.*/v/__p")
         | 
| 201 | 
            +
            			refr="https://fembed-hd.com/v/$fb_id"
         | 
| 202 | 
            +
            			[ -z "$fb_id" ] && return 0
         | 
| 203 | 
            +
            			result_links="$(curl -A "$agent" -s -X POST "https://fembed-hd.com/api/source/$fb_id" -H "x-requested-with:XMLHttpRequest" |
         | 
| 204 | 
            +
            				sed -e 's/\\//g' -e 's/.*data"://' | tr "}" "\n" | sed -nE 's/.*file":"(.*)","label":"(.*)","type.*/\2>\1/p')"
         | 
| 205 | 
            +
            			;;
         | 
| 206 | 
            +
            		1)
         | 
| 207 | 
            +
            			provider_name='Animixplay'
         | 
| 208 | 
            +
            			progress "Fetching $provider_name Direct link.."
         | 
| 209 | 
            +
            			refr="$base_url"
         | 
| 210 | 
            +
            			[ -z "$id" ] && return 0
         | 
| 211 | 
            +
            			enc_id=$(printf "%s" "$id" | base64)
         | 
| 212 | 
            +
            			ani_id=$(printf "%sLTXs3GrU8we9O%s" "$id" "$enc_id" | base64)
         | 
| 213 | 
            +
            			result_links="$(curl -s "$base_url/api/cW9${ani_id}" -A "$agent" -I | sed -nE 's_[L|l]ocation: https?://[^#]*#([^#]*).*_\1_p' | base64 -d)"
         | 
| 214 | 
            +
            			;;
         | 
| 215 | 
            +
            		*)
         | 
| 216 | 
            +
            			provider_name='Gogoanime'
         | 
| 217 | 
            +
            			progress "Fetching $provider_name Direct link.."
         | 
| 218 | 
            +
            			refr="$gogohd_url"
         | 
| 219 | 
            +
            			[ -z "$id" ] && return 0
         | 
| 220 | 
            +
            			secret_key=$(printf "%s" "$resp" | sed -n '2p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n")
         | 
| 221 | 
            +
            			echo "secret_key: $secret_key"
         | 
| 222 | 
            +
            			iv=$(printf "%s" "$resp" | sed -n '3p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n")
         | 
| 223 | 
            +
            			echo "iv: $iv"
         | 
| 224 | 
            +
            			second_key=$(printf "%s" "$resp" | sed -n '4p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n")
         | 
| 225 | 
            +
            			echo "second_key: $second_key"
         | 
| 226 | 
            +
            			token=$(printf "%s" "$resp" | head -1 | base64 -d | openssl enc -d -aes256 -K "$secret_key" -iv "$iv" | sed -nE 's/.*&(token.*)/\1/p')
         | 
| 227 | 
            +
            			echo "token: $token"
         | 
| 228 | 
            +
            			ajax=$(printf '%s' "$id" | openssl enc -e -aes256 -K "$secret_key" -iv "$iv" -a)
         | 
| 229 | 
            +
            			echo "ajax: $ajax"
         | 
| 230 | 
            +
            			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')
         | 
| 231 | 
            +
            			echo "data: $data"
         | 
| 232 | 
            +
            			result_links="$(printf '%s' "$data" | base64 -d 2>/dev/null | openssl enc -d -aes256 -K "$second_key" -iv "$iv" 2>/dev/null |
         | 
| 233 | 
            +
            				tr -d \\\\ | sed -nE "s_.*file\":\"([^\"]*)\".*source.*_\1_p")"
         | 
| 234 | 
            +
            			echo "result_links: $result_links"
         | 
| 235 | 
            +
            			;;
         | 
| 236 | 
            +
             | 
| 237 | 
            +
            	esac
         | 
| 238 | 
            +
            }
         | 
| 239 | 
            +
             | 
| 240 | 
            +
            # chooses the link for the set quality
         | 
| 241 | 
            +
            get_video_link() {
         | 
| 242 | 
            +
            	dpage_url="$1"
         | 
| 243 | 
            +
            	id=$(printf "%s" "$dpage_url" | sed -nE 's/.*id=([^&]*).*/\1/p')
         | 
| 244 | 
            +
            	#multiple sed are used (regex seperated by ';') for extracting only required data from response of embed url
         | 
| 245 | 
            +
            	resp="$(curl -A "$agent" -sL "${gogohd_url}streaming.php?id=$id" |
         | 
| 246 | 
            +
            		sed -nE 's/.*class="container-(.*)">/\1/p ;
         | 
| 247 | 
            +
            			s/.*class="wrapper container-(.*)">/\1/p ;
         | 
| 248 | 
            +
            			s/.*class=".*videocontent-(.*)">/\1/p ;
         | 
| 249 | 
            +
            			s/.*data-value="(.*)">.*/\1/p ;
         | 
| 250 | 
            +
            			s/.*data-status="1".*data-video="(.*)">.*/\1/p')"
         | 
| 251 | 
            +
            	provider=1
         | 
| 252 | 
            +
            	[ -n "$select_provider" ] && provider="$select_provider"
         | 
| 253 | 
            +
            	i=0
         | 
| 254 | 
            +
            	while [ "$i" -lt 3 ] && [ -z "$result_links" ];do
         | 
| 255 | 
            +
            		generate_link "$provider"
         | 
| 256 | 
            +
            		provider=$((provider % 3 + 1))
         | 
| 257 | 
            +
            		if [ "$debug" -eq 1 ]; then
         | 
| 258 | 
            +
            			debug "$refr" "$result_links"
         | 
| 259 | 
            +
            			unset result_links
         | 
| 260 | 
            +
            		fi
         | 
| 261 | 
            +
            		: $((i+=1))
         | 
| 262 | 
            +
            	done
         | 
| 263 | 
            +
            	[ "$debug" -eq 1 ] && return 0
         | 
| 264 | 
            +
            	if printf '%s' "$result_links" | grep -q "m3u8"; then
         | 
| 265 | 
            +
            		get_video_quality_m3u8 "$result_links"
         | 
| 266 | 
            +
            	else
         | 
| 267 | 
            +
            		video_url=$(get_video_quality_mp4 "$result_links")
         | 
| 268 | 
            +
            	fi
         | 
| 269 | 
            +
            	unset result_links
         | 
| 270 | 
            +
            }
         | 
| 271 | 
            +
             | 
| 272 | 
            +
            get_video_quality_mp4() {
         | 
| 273 | 
            +
            	case $quality in
         | 
| 274 | 
            +
            		best)
         | 
| 275 | 
            +
            			video_url=$(printf '%s' "$1" | tail -n 1 | cut -d">" -f2) ;;
         | 
| 276 | 
            +
            		worst)
         | 
| 277 | 
            +
            			video_url=$(printf '%s' "$1" | head -n 1 | cut -d">" -f2) ;;
         | 
| 278 | 
            +
            		*)
         | 
| 279 | 
            +
            			video_url=$(printf '%s' "$1" | grep -i "${quality}p" | head -n 1 | cut -d">" -f2)
         | 
| 280 | 
            +
            			if [ -z "$video_url" ]; then
         | 
| 281 | 
            +
            				err "Current video quality is not available (defaulting to best quality)"
         | 
| 282 | 
            +
            				video_url=$(printf '%s' "$1" | tail -n 1 | cut -d">" -f2)
         | 
| 283 | 
            +
            			fi
         | 
| 284 | 
            +
            			;;
         | 
| 285 | 
            +
            	esac
         | 
| 286 | 
            +
            	printf '%s' "$video_url"
         | 
| 287 | 
            +
            }
         | 
| 288 | 
            +
             | 
| 289 | 
            +
            get_video_quality_m3u8() {
         | 
| 290 | 
            +
            	printf '%s' "$1" | grep -qE "manifest.*m3u.*" && video_url=$1 && return 0
         | 
| 291 | 
            +
            	m3u8_links=$(curl -A "$agent" -s --referer "$dpage_link" "$1")
         | 
| 292 | 
            +
            	case $quality in
         | 
| 293 | 
            +
            		best)
         | 
| 294 | 
            +
            			res_selector=$(printf "%s" "$m3u8_links" | sed -nE 's_.*RESOLUTION=.*x([^,]*).*_\1_p' | sort -nr | head -1);;
         | 
| 295 | 
            +
            		worst)
         | 
| 296 | 
            +
            			res_selector=$(printf "%s" "$m3u8_links" | sed -nE 's_.*RESOLUTION=.*x([^,]*).*_\1_p' | sort -nr | tail -1);;
         | 
| 297 | 
            +
            		*)
         | 
| 298 | 
            +
            			res_selector=$quality
         | 
| 299 | 
            +
            			if ! (printf '%s' "$m3u8_links" | grep -q "x$quality"); then
         | 
| 300 | 
            +
            				err "Current video quality is not available (defaulting to best quality)"
         | 
| 301 | 
            +
            				res_selector=$(printf "%s" "$m3u8_links" | sed -nE 's_.*RESOLUTION=.*x([^,]*).*_\1_p' | sort -nr | head -1)
         | 
| 302 | 
            +
            			fi
         | 
| 303 | 
            +
            			;;
         | 
| 304 | 
            +
            	esac
         | 
| 305 | 
            +
            	video_url=$(printf '%s' "$m3u8_links" | sed -n "/x$res_selector/{n;p;}" | tr -d '\r')
         | 
| 306 | 
            +
            	printf "%s" "$m3u8_links" | grep -q "http" || video_url="$(printf "%s" "$1" | sed 's|[^/]*$||')$video_url" || true
         | 
| 307 | 
            +
            }
         | 
| 308 | 
            +
             | 
| 309 | 
            +
             | 
| 310 | 
            +
            #################
         | 
| 311 | 
            +
            # INPUT PARSING #
         | 
| 312 | 
            +
            #################
         | 
| 313 | 
            +
             | 
| 314 | 
            +
            append () {
         | 
| 315 | 
            +
            	[ -z "$1" ] || printf  "%s\n" "$1"
         | 
| 316 | 
            +
            	printf "%s %s" "$3" "$2"
         | 
| 317 | 
            +
            }
         | 
| 318 | 
            +
             | 
| 319 | 
            +
            # only lets the user pass in case of a valid search
         | 
| 320 | 
            +
            process_search () {
         | 
| 321 | 
            +
            	progress "Searching $scrape.."
         | 
| 322 | 
            +
            	search_results=$(search_anime "$query")
         | 
| 323 | 
            +
            	while [ -z "$search_results" ]; do
         | 
| 324 | 
            +
            		retry 'No search results found'
         | 
| 325 | 
            +
            		query="$REPLY $REPLY2"
         | 
| 326 | 
            +
            		progress "Searching $scrape.."
         | 
| 327 | 
            +
            		search_results=$(search_anime "$query")
         | 
| 328 | 
            +
            	done
         | 
| 329 | 
            +
            	anime_selection "$search_results"
         | 
| 330 | 
            +
            	episode_selection
         | 
| 331 | 
            +
            }
         | 
| 332 | 
            +
             | 
| 333 | 
            +
            # anime-selection menu handling function
         | 
| 334 | 
            +
            anime_selection () {
         | 
| 335 | 
            +
            	if [ "$fzf" -eq 0 ];then
         | 
| 336 | 
            +
            		inf "$scrape Results >>"
         | 
| 337 | 
            +
            	else
         | 
| 338 | 
            +
            		progress ""
         | 
| 339 | 
            +
            	fi
         | 
| 340 | 
            +
            	count=1
         | 
| 341 | 
            +
            	unset selection_list
         | 
| 342 | 
            +
            	while read -r anime_id; do
         | 
| 343 | 
            +
            		displayed_title=$(printf '%s' "$anime_id" | tr '-' ' ')
         | 
| 344 | 
            +
            		selection_list=$(append "$selection_list" "$displayed_title" "$count")
         | 
| 345 | 
            +
            		: $((count+=1))
         | 
| 346 | 
            +
            	done <<-EOF
         | 
| 347 | 
            +
            	$search_results
         | 
| 348 | 
            +
            	EOF
         | 
| 349 | 
            +
            	if [ -n "$select_first" ]; then
         | 
| 350 | 
            +
            		tput clear
         | 
| 351 | 
            +
            		choice=1
         | 
| 352 | 
            +
            	elif [ -z "$ep_choice_to_start" ] || { [ -n "$ep_choice_to_start" ] && [ -z "$select_first" ]; }; then
         | 
| 353 | 
            +
            		selection_list=$(append "$selection_list" "quit" "q")
         | 
| 354 | 
            +
            	if [ "$fzf" -eq 1 ]; then
         | 
| 355 | 
            +
            		choice=$(selection_menu_fzf ". $scrape Results>>" "$selection_list")
         | 
| 356 | 
            +
            		[ -z "$choice" ] && exit 0
         | 
| 357 | 
            +
            	else
         | 
| 358 | 
            +
            		selection_menu "$selection_list"
         | 
| 359 | 
            +
            		choice="$REPLY"
         | 
| 360 | 
            +
            		[ -z "$choice" ] && choice=1
         | 
| 361 | 
            +
            	fi
         | 
| 362 | 
            +
            		while ! [ "$choice" -eq "$choice" ] 2>/dev/null || [ "$choice" -lt 1 ] || [ "$choice" -ge "$count" ] || [ "$choice" = " " ]; do
         | 
| 363 | 
            +
            			[ "$choice" = "q" ] && exit 0
         | 
| 364 | 
            +
            			retry "Invalid choice entered"
         | 
| 365 | 
            +
            			choice="$REPLY"
         | 
| 366 | 
            +
            		done
         | 
| 367 | 
            +
            	fi
         | 
| 368 | 
            +
            	# Select respective anime_id
         | 
| 369 | 
            +
            	selection_id="$(printf "%s" "$search_results" | sed -n "${choice}p" | cut -d':' -f1 | tr -d ' ')"
         | 
| 370 | 
            +
            	progress "Searching Episodes.."
         | 
| 371 | 
            +
            	episode_list "$selection_id"
         | 
| 372 | 
            +
            }
         | 
| 373 | 
            +
             | 
| 374 | 
            +
            # gets episode number from user, makes sure it's in range, skips input if only one episode exists
         | 
| 375 | 
            +
            episode_selection () {
         | 
| 376 | 
            +
            	[ "$last_ep_number" -eq 0 ] && die "Episodes not released yet!"
         | 
| 377 | 
            +
            	if [ "$last_ep_number" -gt "$first_ep_number" ]; then
         | 
| 378 | 
            +
            		[ "$ep_choice_to_start" = "-1" ] && ep_choice_to_start="$last_ep_number"
         | 
| 379 | 
            +
            		if [ -z "$ep_choice_to_start" ]; then
         | 
| 380 | 
            +
            			# if branches, because order matters this time
         | 
| 381 | 
            +
            			while : ; do
         | 
| 382 | 
            +
            				last_ep_number=$(printf '%s' "$last_ep_number"|tr -d "[:space:]")
         | 
| 383 | 
            +
            				prompt "To specify a range, use: start_number end_number (Episodes: $first_ep_number-$last_ep_number)"
         | 
| 384 | 
            +
            				ep_choice_start="$REPLY"
         | 
| 385 | 
            +
            				ep_choice_end="$REPLY2"
         | 
| 386 | 
            +
            				[ "$REPLY" = q ] && exit 0
         | 
| 387 | 
            +
            				[ "$ep_choice_start" = "-1" ] && ep_choice_start="$last_ep_number" || [ -z "$ep_choice_start" ] && ep_choice_start="$last_ep_number"
         | 
| 388 | 
            +
            				[ "$ep_choice_end" = "-1" ] && ep_choice_end="$last_ep_number"
         | 
| 389 | 
            +
            				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
         | 
| 390 | 
            +
            					err "Invalid number(s)"
         | 
| 391 | 
            +
            					continue
         | 
| 392 | 
            +
            				fi
         | 
| 393 | 
            +
            				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
         | 
| 394 | 
            +
            					err "Episode out of range"
         | 
| 395 | 
            +
            					continue
         | 
| 396 | 
            +
            				fi
         | 
| 397 | 
            +
            				if [ -n "$ep_choice_end" ] && [ "$ep_choice_end" -le "$ep_choice_start" ]; then
         | 
| 398 | 
            +
            					err "Invalid range"
         | 
| 399 | 
            +
            					continue
         | 
| 400 | 
            +
            				fi
         | 
| 401 | 
            +
            				break
         | 
| 402 | 
            +
            			done
         | 
| 403 | 
            +
            		else
         | 
| 404 | 
            +
            			ep_choice_start="$ep_choice_to_start" && unset ep_choice_to_start
         | 
| 405 | 
            +
            		fi
         | 
| 406 | 
            +
            	else
         | 
| 407 | 
            +
            		# In case the anime contains only a single episode
         | 
| 408 | 
            +
            		ep_choice_start=1
         | 
| 409 | 
            +
            	fi
         | 
| 410 | 
            +
            	[ -n "$ep_choice_end" ] && auto_play=1
         | 
| 411 | 
            +
            }
         | 
| 412 | 
            +
             | 
| 413 | 
            +
            # creates $episodes from $ep_choice_start and $ep_choice_end
         | 
| 414 | 
            +
            generate_ep_list() {
         | 
| 415 | 
            +
            	episodes=$ep_choice_start
         | 
| 416 | 
            +
            	[ -n "$ep_choice_end" ] && episodes=$(seq "$ep_choice_start" "$ep_choice_end")
         | 
| 417 | 
            +
            }
         | 
| 418 | 
            +
             | 
| 419 | 
            +
             | 
| 420 | 
            +
            ##################
         | 
| 421 | 
            +
            # VIDEO PLAYBACK #
         | 
| 422 | 
            +
            ##################
         | 
| 423 | 
            +
             | 
| 424 | 
            +
            # opens selected episodes one-by-one
         | 
| 425 | 
            +
            open_selection() {
         | 
| 426 | 
            +
            	for ep in $episodes; do
         | 
| 427 | 
            +
            		open_episode "$selection_id" "$ep"
         | 
| 428 | 
            +
            	done
         | 
| 429 | 
            +
            	episode=${ep_choice_end:-$ep_choice_start}
         | 
| 430 | 
            +
            }
         | 
| 431 | 
            +
             | 
| 432 | 
            +
            open_episode () {
         | 
| 433 | 
            +
            	anime_id="$1"
         | 
| 434 | 
            +
            	episode="$2"
         | 
| 435 | 
            +
            	tput clear
         | 
| 436 | 
            +
            	progress "Loading episode $episode..."
         | 
| 437 | 
            +
            	# decrypting url
         | 
| 438 | 
            +
            	dpage_link=$(printf "%s" "$select_ep_result" | sed -n "${episode}p")
         | 
| 439 | 
            +
            	if [ -z "$dpage_link" ];then
         | 
| 440 | 
            +
            		die "Episode doesn't exist!!"
         | 
| 441 | 
            +
            	else
         | 
| 442 | 
            +
            		get_video_link "$dpage_link"
         | 
| 443 | 
            +
            	fi
         | 
| 444 | 
            +
            	[ "$debug" -eq 1 ] && exit 0
         | 
| 445 | 
            +
            	# write anime and episode number and save to temporary history
         | 
| 446 | 
            +
            	grep -q -- "$selection_id" "$logfile" || printf "%s\t%s\n" "$selection_id" $((episode+1)) >> "$logfile"
         | 
| 447 | 
            +
            	sed -E "s/^${selection_id}\t[0-9]*/${selection_id}\t$((episode+1))/" "$logfile" > "${logfile}.new"
         | 
| 448 | 
            +
            	[ ! "$PID" = "0" ] && kill "$PID" >/dev/null 2>&1
         | 
| 449 | 
            +
            	[ -z "$video_url" ] && die "Video URL not found"
         | 
| 450 | 
            +
            	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')"
         | 
| 451 | 
            +
            	if [ "$auto_play" -eq 0 ]; then
         | 
| 452 | 
            +
            		play_episode "$video_url" "$refr" "$trackma_title"
         | 
| 453 | 
            +
            	else
         | 
| 454 | 
            +
            		printf "\n"
         | 
| 455 | 
            +
            		play_episode "$video_url" "$refr" "$trackma_title"
         | 
| 456 | 
            +
            		wait
         | 
| 457 | 
            +
            		sleep 2
         | 
| 458 | 
            +
            	fi
         | 
| 459 | 
            +
            	PID=$!
         | 
| 460 | 
            +
            	# overwrite history with temporary history
         | 
| 461 | 
            +
            	mv "${logfile}.new" "$logfile"
         | 
| 462 | 
            +
            }
         | 
| 463 | 
            +
             | 
| 464 | 
            +
            play_episode () {
         | 
| 465 | 
            +
            	video_url="$1"; refr="$2" trackma_title="$3"
         | 
| 466 | 
            +
            	if [ "$player_fn" = "download" ];then
         | 
| 467 | 
            +
            		inf "Currently downloading $trackma_title ($provider_name)"
         | 
| 468 | 
            +
            	else
         | 
| 469 | 
            +
            		inf "Currently playing $trackma_title ($provider_name)"
         | 
| 470 | 
            +
            	fi
         | 
| 471 | 
            +
            	case "$player_fn" in
         | 
| 472 | 
            +
            		download)
         | 
| 473 | 
            +
            			if download "$refr" "$video_url" "$trackma_title"; then
         | 
| 474 | 
            +
            				inf "Downloaded episode: $trackma_title"
         | 
| 475 | 
            +
            			else
         | 
| 476 | 
            +
            				err "Download failed episode: $trackma_title , please retry or check your internet connection"
         | 
| 477 | 
            +
            			fi
         | 
| 478 | 
            +
            			;;
         | 
| 479 | 
            +
            		iina)
         | 
| 480 | 
            +
            			nohup "$player_fn" "$video_url" --no-stdin --keep-running --mpv-referrer="$refr" --mpv-force-media-title="$trackma_title" > /dev/null 2>&1 & ;;
         | 
| 481 | 
            +
            		vlc)
         | 
| 482 | 
            +
            			if uname -a | grep -qE '[Aa]ndroid';then
         | 
| 483 | 
            +
            				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 &
         | 
| 484 | 
            +
            			else
         | 
| 485 | 
            +
            				nohup "$player_fn" "$video_url"  --http-referrer="$refr" --meta-title "$trackma_title" --play-and-exit > /dev/null 2>&1 &
         | 
| 486 | 
            +
            			fi
         | 
| 487 | 
            +
            			;;
         | 
| 488 | 
            +
            		"syncplay"|"/Applications/Syncplay.app/Contents/MacOS/syncplay"|"/c/Program Files (x86)/Syncplay/Syncplay.exe")
         | 
| 489 | 
            +
            			nohup "$player_fn" "$video_url" -- --referrer="$refr" --force-media-title="$trackma_title" > /dev/null 2>&1 & ;;
         | 
| 490 | 
            +
            		*)
         | 
| 491 | 
            +
            			if uname -a | grep -qE '[Aa]ndroid';then
         | 
| 492 | 
            +
            				am start --user 0 -a android.intent.action.VIEW -d "$video_url" -n is.xyz.mpv/.MPVActivity > /dev/null 2>&1 &
         | 
| 493 | 
            +
            			else
         | 
| 494 | 
            +
            				nohup "$player_fn" "$video_url" --referrer="$refr" --force-media-title="$trackma_title" > /dev/null 2>&1 &
         | 
| 495 | 
            +
            			fi
         | 
| 496 | 
            +
            			;;
         | 
| 497 | 
            +
            	esac
         | 
| 498 | 
            +
            }
         | 
| 499 | 
            +
             | 
| 500 | 
            +
            ############
         | 
| 501 | 
            +
            # START UP #
         | 
| 502 | 
            +
            ############
         | 
| 503 | 
            +
             | 
| 504 | 
            +
            # clears the colors and deletes temporary logfile when exited using SIGINT
         | 
| 505 | 
            +
            trap 'printf "\033[0m";rm -f "$logfile".new;exit 1' INT HUP
         | 
| 506 | 
            +
             | 
| 507 | 
            +
            # default options
         | 
| 508 | 
            +
            agent="Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0"
         | 
| 509 | 
            +
            PID=0
         | 
| 510 | 
            +
            quality=best
         | 
| 511 | 
            +
            scrape=Query
         | 
| 512 | 
            +
            debug=0
         | 
| 513 | 
            +
            choice=
         | 
| 514 | 
            +
            fzf=0
         | 
| 515 | 
            +
            auto_play=0
         | 
| 516 | 
            +
            download_dir="$(pwd)"
         | 
| 517 | 
            +
            case "$(uname)" in
         | 
| 518 | 
            +
            	Darwin*) player_fn='iina';;
         | 
| 519 | 
            +
            	*) player_fn='mpv';;
         | 
| 520 | 
            +
            esac
         | 
| 521 | 
            +
            # history file path
         | 
| 522 | 
            +
            logdir="${XDG_CACHE_HOME:-$HOME/.cache}"
         | 
| 523 | 
            +
            logfile="$logdir/ani-hsts"
         | 
| 524 | 
            +
            # create history file and history dir if none found
         | 
| 525 | 
            +
            [ -d "$logdir" ] || mkdir "$logdir"
         | 
| 526 | 
            +
            [ -f "$logfile" ] || : > "$logfile"
         | 
| 527 | 
            +
             | 
| 528 | 
            +
            while getopts 'svq:dp:chDUVa:xr:fn' OPT; do
         | 
| 529 | 
            +
            	case $OPT in
         | 
| 530 | 
            +
            		d) player_fn='download' ;;
         | 
| 531 | 
            +
            		a) ep_choice_to_start=$OPTARG ;;
         | 
| 532 | 
            +
            		U) update_script ;;
         | 
| 533 | 
            +
            		D)
         | 
| 534 | 
            +
            			: > "$logfile"
         | 
| 535 | 
            +
            			exit 0
         | 
| 536 | 
            +
            			;;
         | 
| 537 | 
            +
            		p)
         | 
| 538 | 
            +
            			player_fn='download'
         | 
| 539 | 
            +
            			download_dir="$OPTARG"
         | 
| 540 | 
            +
            			if [ ! -d "$download_dir" ] ; then
         | 
| 541 | 
            +
            				mkdir -p "$download_dir" || die "Couldn't create $download_dir"
         | 
| 542 | 
            +
            			fi
         | 
| 543 | 
            +
            			;;
         | 
| 544 | 
            +
            		n) scrape=New ;;
         | 
| 545 | 
            +
            		s)
         | 
| 546 | 
            +
            			case "$(uname -s)" in
         | 
| 547 | 
            +
            				Darwin*) player_fn="/Applications/Syncplay.app/Contents/MacOS/syncplay" ;;
         | 
| 548 | 
            +
            				MINGW*|*Msys) player_fn="/c/Program Files (x86)/Syncplay/Syncplay.exe" ;;
         | 
| 549 | 
            +
            				*) player_fn="syncplay" ;;
         | 
| 550 | 
            +
            			esac
         | 
| 551 | 
            +
            			;;
         | 
| 552 | 
            +
            		q) quality=$OPTARG ;;
         | 
| 553 | 
            +
            		x) debug=1 ;;
         | 
| 554 | 
            +
            		r) select_provider=$OPTARG ;;
         | 
| 555 | 
            +
            		f) fzf=1 ;;
         | 
| 556 | 
            +
            		c) scrape=History ;;
         | 
| 557 | 
            +
            		v) player_fn='vlc';;
         | 
| 558 | 
            +
            		V)
         | 
| 559 | 
            +
            			version_text
         | 
| 560 | 
            +
            			exit 0
         | 
| 561 | 
            +
            			;;
         | 
| 562 | 
            +
            		h)
         | 
| 563 | 
            +
            			help_text
         | 
| 564 | 
            +
            			exit 0
         | 
| 565 | 
            +
            			;;
         | 
| 566 | 
            +
            		*)
         | 
| 567 | 
            +
            			help_text
         | 
| 568 | 
            +
            			exit 1
         | 
| 569 | 
            +
            			;;
         | 
| 570 | 
            +
            	esac
         | 
| 571 | 
            +
            done
         | 
| 572 | 
            +
            shift $((OPTIND - 1))
         | 
| 573 | 
            +
            progress "Checking dependencies.."
         | 
| 574 | 
            +
            # shellcheck disable=SC2046
         | 
| 575 | 
            +
            dep_ch "curl" "sed" "grep" "openssl" || true
         | 
| 576 | 
            +
             | 
| 577 | 
            +
            if [ "$player_fn" = "download" ];then
         | 
| 578 | 
            +
                dep_ch "ffmpeg" "axel"
         | 
| 579 | 
            +
            elif ! (uname -a | grep -qE '[Aa]ndroid');then
         | 
| 580 | 
            +
                dep_ch "$player_fn"
         | 
| 581 | 
            +
            fi
         | 
| 582 | 
            +
             | 
| 583 | 
            +
            gogohd_url="https://gogohd.net/"
         | 
| 584 | 
            +
            base_url="https://animixplay.to"
         | 
| 585 | 
            +
            if [ "$scrape" = "Query" ];then
         | 
| 586 | 
            +
            	if [ -z "$*" ]; then
         | 
| 587 | 
            +
            		prompt "Search Anime"
         | 
| 588 | 
            +
            		query="$REPLY $REPLY2"
         | 
| 589 | 
            +
            	else
         | 
| 590 | 
            +
            		if [ -n "$ep_choice_to_start" ]; then
         | 
| 591 | 
            +
            			REPLY=1
         | 
| 592 | 
            +
            			select_first=1
         | 
| 593 | 
            +
            		fi
         | 
| 594 | 
            +
            		query="$*"
         | 
| 595 | 
            +
            	fi
         | 
| 596 | 
            +
            	process_search
         | 
| 597 | 
            +
            elif [ "$scrape" = "New" ];then
         | 
| 598 | 
            +
            	progress ""
         | 
| 599 | 
            +
            	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)"
         | 
| 600 | 
            +
            	[ -z "$selection_id" ] && die "No anime Selected"
         | 
| 601 | 
            +
            	ep=$(printf "%s" "$selection_id" | sed 's_.*episode __')
         | 
| 602 | 
            +
            	selection_id=$(printf "%s" "$selection_id" | sed 's_ episode.*__' | tr ' ' '-')
         | 
| 603 | 
            +
            	episode_list "$selection_id"
         | 
| 604 | 
            +
            	open_episode "$selection_id" "$ep"
         | 
| 605 | 
            +
            	exit 0
         | 
| 606 | 
            +
            else
         | 
| 607 | 
            +
            	search_history
         | 
| 608 | 
            +
            fi
         | 
| 609 | 
            +
             | 
| 610 | 
            +
            generate_ep_list
         | 
| 611 | 
            +
            open_selection
         | 
| 612 | 
            +
             | 
| 613 | 
            +
            ########
         | 
| 614 | 
            +
            # LOOP #
         | 
| 615 | 
            +
            ########
         | 
| 616 | 
            +
             | 
| 617 | 
            +
            while : ; do
         | 
| 618 | 
            +
            	auto_play=0
         | 
| 619 | 
            +
            	unset menu
         | 
| 620 | 
            +
            	unset options
         | 
| 621 | 
            +
            	[ "$episode" -ne "$last_ep_number" ] && menu=$(append "$menu" 'next' 'n')
         | 
| 622 | 
            +
            	[ "$episode" -ne "$first_ep_number" ] && menu=$(append "$menu" 'previous' 'p')
         | 
| 623 | 
            +
            	menu=$(append "$menu" 'replay' 'r')
         | 
| 624 | 
            +
            	[ "$first_ep_number" -ne "$last_ep_number" ] && menu=$(append "$menu" 'select' 's')
         | 
| 625 | 
            +
            	menu=$(append "$menu" 'quit' 'q')
         | 
| 626 | 
            +
            	if [ "$fzf" -eq 1 ];then
         | 
| 627 | 
            +
            		progress ""
         | 
| 628 | 
            +
            		choice="$(selection_menu_fzf ". Menu>>" "$menu")"
         | 
| 629 | 
            +
            		[ -z "$choice" ] && die "No anime Selected"
         | 
| 630 | 
            +
            	else
         | 
| 631 | 
            +
            		selection_menu "$menu"
         | 
| 632 | 
            +
            			choice="$REPLY"
         | 
| 633 | 
            +
            	fi
         | 
| 634 | 
            +
            	case $choice in
         | 
| 635 | 
            +
            		n|'')
         | 
| 636 | 
            +
            			ep_choice_start=$((episode + 1))
         | 
| 637 | 
            +
            			unset ep_choice_end
         | 
| 638 | 
            +
            			;;
         | 
| 639 | 
            +
            		p)
         | 
| 640 | 
            +
            			ep_choice_start=$((episode - 1))
         | 
| 641 | 
            +
            			unset ep_choice_end
         | 
| 642 | 
            +
            			;;
         | 
| 643 | 
            +
            		r)
         | 
| 644 | 
            +
            			ep_choice_start="$episode"
         | 
| 645 | 
            +
            			unset ep_choice_end
         | 
| 646 | 
            +
            			;;
         | 
| 647 | 
            +
            		s)
         | 
| 648 | 
            +
            			episode_selection ;;
         | 
| 649 | 
            +
            		q)
         | 
| 650 | 
            +
            			break ;;
         | 
| 651 | 
            +
            		*)
         | 
| 652 | 
            +
            			tput clear
         | 
| 653 | 
            +
            			err "Invalid choice"
         | 
| 654 | 
            +
            			continue
         | 
| 655 | 
            +
            			;;
         | 
| 656 | 
            +
            	esac
         | 
| 657 | 
            +
            	generate_ep_list
         | 
| 658 | 
            +
            	open_selection
         | 
| 659 | 
            +
            done
         | 
| 660 | 
            +
             | 
| 661 | 
            +
            # ani-cli
         | 
| 662 | 
            +
            #
         | 
| 663 | 
            +
            # This program is free software: you can redistribute it and/or modify
         | 
| 664 | 
            +
            # it under the terms of the GNU General Public License as published by
         | 
| 665 | 
            +
            # the Free Software Foundation, either version 3 of the License, or
         | 
| 666 | 
            +
            # (at your option) any later version.
         | 
| 667 | 
            +
            #
         | 
| 668 | 
            +
            # This program is distributed in the hope that it will be useful,
         | 
| 669 | 
            +
            # but WITHOUT ANY WARRANTY; without even the implied warranty of
         | 
| 670 | 
            +
            # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
         | 
| 671 | 
            +
            # GNU General Public License for more details.
         | 
| 672 | 
            +
            #
         | 
| 673 | 
            +
            # You should have received a copy of the GNU General Public License
         | 
| 674 | 
            +
            # along with this program. If not, see <http://www.gnu.org/licenses/>.
         | 
| 675 | 
            +
            #
         | 
| 676 | 
            +
            # Project repository: https://github.com/pystardust/ani-cli
         |