ani-cli-npm 2.1.4 → 2.1.5
Sign up to get free protection for your applications and to get access to all the features.
- package/.idea/ani-cli-npm.iml +11 -11
- package/.idea/discord.xml +6 -6
- package/.idea/git_toolbox_prj.xml +15 -0
- package/.idea/inspectionProfiles/Project_Default.xml +10 -0
- package/.idea/jsLibraryMappings.xml +5 -5
- package/.idea/modules.xml +7 -7
- package/.idea/vcs.xml +5 -5
- package/README.MD +79 -77
- package/bin/Anime.js +409 -393
- package/bin/IO/help.js +59 -0
- package/bin/IO/input.js +89 -0
- package/bin/cache.js +79 -0
- package/bin/change_config.js +99 -0
- package/bin/core_utils/curl.js +35 -35
- package/bin/core_utils/interfaces.js +2 -2
- package/bin/core_utils/libs.js +12 -12
- package/bin/core_utils/regex.js +8 -8
- package/bin/cover_manager.js +3 -0
- package/bin/curl.js +35 -0
- package/bin/download.js +36 -36
- package/bin/file_managment/cache.js +87 -87
- package/bin/file_managment/change_config.js +122 -121
- package/bin/file_managment/load_config.js +111 -110
- package/bin/generate_link.js +47 -47
- package/bin/help.js +59 -59
- package/bin/index.js +164 -159
- package/bin/input.js +89 -89
- package/bin/interfaces.js +2 -0
- package/bin/libs.js +12 -0
- package/bin/load_config.js +106 -0
- package/bin/regex.js +8 -0
- package/bin/search_anime.js +39 -39
- package/bin/url_genoration/generate_link.js +47 -0
- package/bin/url_genoration/search_anime.js +39 -0
- package/bin/video_quality.js +6 -0
- package/package.json +45 -45
- 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/tsconfig.json +109 -109
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
|