-
Notifications
You must be signed in to change notification settings - Fork 3
/
hls
executable file
·126 lines (114 loc) · 4.76 KB
/
hls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/bin/sh
#initializing..
help_text () {
while IFS= read -r line; do
printf "%s\n" "$line"
done <<-EOF
Usage:
${0##*/} [ -o <filename> ] [ -r | -f | -n <no. of connections>] [ <m3u8_link> ]
${0##*/} -h
Options:
-h show helptext
-o filename (default : video)
-r select highest resolution automatically
-n set maximum number of connections (default : 36)
-f skip ffmpeg file conversion (used to enable the video file to run on any video player)
-s subtitles url (will be saved as same name as the video file)
EOF
}
download(){
printf "" > "$failed"
for i in $1; do
curl --max-time 30 -s "${relative_url}$(printf "%s" "$data" | sed -n "${i}p")" > "$tmpdir/$(printf "%05d" "$i").ts" && printf "\033[2K\r\033[1;32m ✓ %s / %s done" "$i" "$range" || printf "%s\n" "$i" >> "$failed" &
jobs -p > "$jobdir"
while [ "$(wc -l "$jobdir")" -ge "$n" ];do jobs > "$jobdir";sleep 0.05;done
done
wait
}
n=36 #no. of parallel download or connections
file="video" #default filename
tmpdir="${XDG_CACHE_HOME:-$HOME/.cache}/hls-temp"
jobdir="${XDG_CACHE_HOME:-$HOME/.cache}/hls-jobs"
failed="${XDG_CACHE_HOME:-$HOME/.cache}/hls-fail"
while getopts 'o:rfhn:s:' OPT; do
case $OPT in
o) file=$OPTARG ;;
n) n=$OPTARG ;;
f) skip_ffmpeg=1;;
r) skip_res=1;;
s) subs=$OPTARG;;
*)
help_text
exit 0
;;
esac
done
shift $((OPTIND - 1))
[ -z "$*" ] && printf "\033[1;34mEnter link >\033[0m " && read -r link || link=$*
trap "killall curl;rm -rdf '$tmpdir' '$jobdir';exit 0" INT HUP
printf "\033[2K\r\033[1;36mFetching resolutions.."
m3u8_data=$(curl -s "$link")
res_list=$(printf "%s" "$m3u8_data" | sed -nE 's_.*RESOLUTION=.*x([^,]*).*_\1_p')
if [ -n "$res_list" ];then
highest_res=$(printf "%s" "$res_list" | sort -nr | head -1)
[ -z "$skip_res" ] && printf "\033[2K\r\033[1;33mRESOLUTIONS >>\n\033[0m%s\n\033[1;34mType ur preferred resolution (default: %s) > " "$res_list" "$highest_res" && read -r sel_res || printf "\033[2K\r\033[1;36mSelecting highest resolution.."
[ -z "$sel_res" ] && sel_res=$highest_res
unset highest_res res_list
url=$(printf "%s" "$m3u8_data" | sed -n "/x$sel_res/{n;p;}" | tr -d '\r')
#check whether the m3u8_data contains uri that starts from http
printf "%s" "$url" | grep -q "http" || relative_url=$(printf "%s" "$link" | sed 's|[^/]*$||')
printf "\033[2K\r\033[1;36mFetching Metadata.."
url="${relative_url}$url"
resp="$(curl -s "$url")"
else
url=$link
resp=$m3u8_data
fi
[ -d "$tmpdir" ] || mkdir -p "$tmpdir"
#extract key uri and iv uri from encrypted stream if exists..
key_uri="$(printf "%s" "$resp" | sed -nE 's/^#EXT-X-KEY.*URI="([^"]*)"/\1/p')"
[ -z "$key_uri" ] || iv_uri="$(printf "%s" "$resp" | sed -nE 's/^#EXT-X-IV.*URI="([^"]*)"/\1/p')"
data="$(printf "%s" "$resp" | sed '/#/d')"
printf "%s" "$data" | grep -q "http" && relative_url='' || relative_url=$(printf "%s" "$url" | sed 's|[^/]*$||')
range=$(printf "%s\n" "$data" | wc -l)
#for encrypted stream only
if [ -n "$key_uri" ];then
#extract key from uri..
key=$(curl -s "$key_uri" | od -A n -t x1 | tr -d ' |\n')
#iv from uri
[ -z "$iv_uri" ] && iv=$(openssl rand -hex 16) || iv=$(curl -s "$iv_uri" | od -A n -t x1 | tr -d ' |\n')
fi
printf "\033[2K\r\033[1;35mpieces : %s\n\033[1;33mDownloading.." "$range"
#downloading .ts data asynchronously
if command -v aria2c >>/dev/null;then
printf '%s' "$data" | nl -n'rz' | sed -E "s|^([0-9]*)[[:space:]]*(.*)|${relative_url}\2\n\tout=\1.ts|g" | aria2c -x16 -s16 -j "$n" -k'1M' -d "$tmpdir" -i - --download-result=hide --summary-interval=0 --allow-overwrite=true
else
download "$(seq "$range")"
#redownloading failed pieces
download "$(cat "$failed")"
fi
#downloading subtitles if uri passed using -s option
[ -z "$subs" ] || curl -s "$subs" -o "$file.srt" &
#concatenating all .ts file in one file..
if [ -n "$key_uri" ];then
#decrypting while concatenating..
printf "\033[2K\r\033[1;36m Decrypting and Concatenating pieces into single file.."
for i in "$tmpdir"/*;do
openssl aes-128-cbc -d -K "$key" -iv "$iv" -nopad >> "$file.ts" < "$i"
done
else
printf "\033[2K\r\033[1;36m Concatenating pieces into single file..\n"
cat "$tmpdir"/* | ffmpeg -loglevel error -stats -i - -c copy "$file.mp4" -y
skip_ffmpeg=1
fi
rm -rdf "$tmpdir" "$jobdir" "$failed"
#conversion of allts file to mp4 video using ffmpeg..
if [ -z "$skip_ffmpeg" ];then
printf "\033[2K\r\033[1;36mEncoding file to mp4 video..\n\033[0m"
ffmpeg -i "$file.ts" -loglevel error -stats -c copy "$file.mp4"
else
[ -f "$file.ts" ] && mv "$file.ts" "$file.mp4"
fi
#cleanup..
rm -f "$file".ts
printf "\033[2K\r\033[1;36m Done!!"