-
Notifications
You must be signed in to change notification settings - Fork 5
/
yt
executable file
·354 lines (321 loc) · 12 KB
/
yt
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
#!/usr/bin/env bash
yt() {
local usage="NAME
yt - fine-tuning the use of youtube-dl / yt-dlp. Download music or video from e.g.
YouTube, Soundcloud, Instagram, Facebook. For a full list of supported sites and
services, see https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md.
SYNOPSIS
yt [OPTIONS] -- [URL] [URL...]
DESCRIPTION
yt is a bash function that optimizes the use of yt-dlp for audio and videophiles
with comprehensive and customizable presets, empirically tested on multiple streams.
Maintains a download archive, to prevent duplicates when periodically downloading
YouTube playlists or Soundcloud sets. Parses title (\"%(artist)s - %(title)s\") and
retrieves a thumbnail, and injects these into file metadata. Adds the url id of the
source to the filename, and attempts to bypass geographical restrictions.
OPTIONS
-h, --help
Print this help text and exit.
-s
Enable silent mode (send stdout to /dev/null).
-S
Enable sequential mode. Default behaviour: parallel mode. The MAXPROCS (env)
var sets parallelism (default 4). To download YouTube playlists in parallel,
use e.g. \"yt -v -- \$(yt-dlp --get-id --flat-playlist <playlist-url>)\".
-f
Force download, even when already recorded in --download-archive.
-v
Enable video mode. Defaults to audio mode. Only mono and stereo are supported.
-c
Fetch space separated URLs from clipboard, additional to the manually passed
URLs. Auto-enables when no URLs are manually passed.
-D POSIX_PATH
Set the destination path. Used for both the (intermediate) output and for the
download archive. Defaults to \"~/Music/yt\" and \"~/Movies/yt\" for audio and
video mode respectively. Override defaults with YT_MUSIC_DIR and YT_VIDEO_DIR.
-p
Enable playlist mode. When a video URL contains a reference to a playlist, the
whole playlist will be downloaded. Will only download URLs that have not yet
been recorded in the download archive.
-k
Keep original audio additionally. In most cases, this will keep e.g. OPUS for
YouTube, or LAME MP3 / WAV for Soundcloud URLs. Ignored when -v is specified.
-a KBITS_PER_SECOND
Set the output audio bitrate. Defaults to 256kbit/s.
-r HERTZ
Set the output audio sampling rate. Defaults to 44100Hz.
-P PIXELS
Set the maximum pixels of the video output. Ignored when -v is not specified.
Defaults to 1920px. Constraint is dropped when no formats comply. For portrait
and landscape videos, this corresponds to the height and width respectively.
-F FRAMES
Set the maximum framerate in frames per second. Defaults to 42. Ignored when
-v is not specified. Constraint is dropped when no formats comply.
-m
Use MP4 when merging audio/video streams, keeping video codecs if possible and
converting audio to 256kbit/s AAC (resolving full 44.1KHz stream). If no merge
is needed, the (single) source file is kept and no conversion is performed.
Default behaviour: copy downloaded audio/video streams into an MKV container,
using OPUS audio codec and VP9 video codec for small filesizes. Ignored when
-v is not specified. For YouTube this will yield a maximum resolution of 1080.
Sometimes, AV1 streams will only be available up to a certain resolution. In
this case, specifying -M might yield higher resolution.
-M
Prefer the older AVC codec over AV1. Results in bigger file-sizes, but better
playback compatibility. Ignored when -v and -m are not specified.
-H
Prefer HDR streams. Tested on YouTube videos. Overrides -m.
-A
Embed all available audio streams. Ignored when -v is not specified.
BSD 3-Clause License
Copyright (c) 2019, ddelange
All rights reserved."
local OPTIND # https://stackoverflow.com/a/16655341/5511061
local SILENT=false
local PARALLEL=true
local PARALLELISM=${MAXPROCS:=4}
local VIDEO_MODE=false
local CLIPBOARD=false
local CUSTOM_DESTINATION=
local PLAYLIST=false
local KEEP_AUDIO=false
local AUDIO_MULTISTREAMS=false
local FORCE=false
local CUSTOM_AUDIO_BITRATE=false
local AUDIO_BITRATE=256
local AUDIO_SAMPLING_RATE=44100
local MAX_PIXELS=1280 # Youtube Shorts are 720x1280 (portrait)
local MAX_FPS=42
local MP4=false
local AVC=false
local HDR=false
local URLS=()
local CODECS=$(ffmpeg -codecs -hide_banner)
if echo ${CODECS} | grep -q 'aac_at'; then
local ENCODER='aac_at'
elif echo ${CODECS} | grep -q 'libfdk_aac'; then
local ENCODER='libfdk_aac'
else
local ENCODER='aac'
fi
# TODO
# ~$ sudo yt -U
# sudo: yt: command not found
# TODO
# Support playlists in parallel https://github.com/ytdl-org/youtube-dl/issues/3746#issuecomment-446694257
if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
set -- "-h"
fi
while getopts ":hUsSfvcD:pka:r:P:mMAH" opt; do
case $opt in
h)
echo "$usage"
return;;
s)
SILENT=true
;;
S)
PARALLEL=false
;;
f)
FORCE=true
;;
v)
VIDEO_MODE=true
;;
c)
CLIPBOARD=true
;;
D)
CUSTOM_DESTINATION="$OPTARG"
;;
p)
PLAYLIST=true
;;
k)
KEEP_AUDIO=true
;;
a)
case $OPTARG in
''|*[!0-9]*)
echo "-a: specify bitrate as integer. Type \"yt -h\" for more info."
return
;;
*)
if [ $OPTARG -gt 1411 ]; then
echo "-a: bitrate should not be greater than 1411kbit/s."
return
fi
CUSTOM_AUDIO_BITRATE=true
AUDIO_BITRATE=$OPTARG
;;
esac
;;
r)
case $OPTARG in
''|*[!0-9]*)
echo "-r: specify sampling rate as integer. Type \"yt -h\" for more info."
return
;;
*)
if [ $OPTARG -gt 48000 ]; then
echo "-r: sampling rate should not be greater than 48000Hz."
return
fi
AUDIO_SAMPLING_RATE=$OPTARG
;;
esac
;;
P)
case $OPTARG in
''|*[!0-9]*)
echo "-P: specify pixels as integer. Type \"yt -h\" for more info."
return
;;
*)
if [ $OPTARG -lt 144 ]; then
echo "-P: pixels should not be less than 144px."
return
fi
MAX_PIXELS=$OPTARG
;;
esac
;;
F)
case $OPTARG in
''|*[!0-9]*)
echo "-F: specify fps as integer. Type \"yt -h\" for more info."
return
;;
*)
if [ $OPTARG -lt 20 ]; then
echo "-F: fps should not be less than 20."
return
fi
MAX_FPS=$OPTARG
;;
esac
;;
m)
MP4=true
;;
M)
AVC=true
;;
H)
HDR=true
;;
A)
AUDIO_MULTISTREAMS=true
;;
\?)
echo "Invalid option: -$OPTARG"
return;;
:)
echo "Option -$OPTARG requires an argument. Type \"yt -h\" for more info."
return;;
esac
done
shift "$((OPTIND - 1))"
# get remaining arguments, treated as urls
while test $# -gt 0; do
URLS+=("$1")
shift
done
if $CLIPBOARD || [ ${#URLS[@]} -eq 0 ]; then
URLS+=($(pbpaste))
fi
if ! $SILENT; then
echo "${URLS[@]}"
fi
# set paths
if [[ $CUSTOM_DESTINATION ]]; then
local destination="$CUSTOM_DESTINATION"
else
if $VIDEO_MODE; then
local destination="${YT_VIDEO_DIR:-~/Movies/yt}"
else
local destination="${YT_MUSIC_DIR:-~/Music/yt}"
fi
fi
# ensure trailing slash on $destination
local len=$((${#destination}-1))
if [ "${destination:len}" != "/" ]; then
destination="${destination}/"
fi
# BASE_OPTIONS
local BASE_OPTIONS=(--geo-bypass --ignore-config -i -F --no-simulate)
local output_filename="${destination}%(title).200s %(id)s.%(ext)s"
local download_archive="${destination}downloaded.txt"
BASE_OPTIONS+=(-o "$output_filename")
if ! $FORCE; then
BASE_OPTIONS+=(--download-archive "$download_archive")
else
BASE_OPTIONS+=(--force-overwrites)
fi
BASE_OPTIONS+=(--add-metadata --metadata-from-title "%(artist)s - %(title)s")
if $PLAYLIST; then
BASE_OPTIONS+=(--yes-playlist)
else
BASE_OPTIONS+=(--no-playlist)
fi
# DOWNLOAD_OPTIONS
local audio_selector='bestaudio[acodec=opus]/bestaudio[container*=dash]/bestaudio'
local px_spec="[height<=${MAX_PIXELS}][fps<=${MAX_FPS}]"
local hdr_selector="bestvideo[vcodec~='(vp09|vp9)']${px_spec}/bestvideo${px_spec}/bestvideo"
local avc_selector="bestvideo[vcodec^=avc]${px_spec}/bestvideo"'[vcodec!^=vp]'"${px_spec}/bestvideo[ext=mp4]"
local av1_selector="bestvideo[vcodec^=av01]${px_spec}/bestvideo[vcodec^=av]${px_spec}/bestvideo"'[vcodec!^=vp]'"${px_spec}/bestvideo[ext=mp4]"
local default_video_selector="bestvideo[vcodec~='(vp09.00|vp9)']${px_spec}/bestvideo[vcodec!=vp09.02]${px_spec}/bestvideo${px_spec}/bestvideo"
local fallback_video_selector="best${px_spec}/best"
if $VIDEO_MODE; then
if $AUDIO_MULTISTREAMS; then
audio_selector='mergeall[vcodec=none]'
fi
if $HDR; then
local DOWNLOAD_OPTIONS=(--merge-output-format mkv -f "(${hdr_selector})+(${audio_selector})/${fallback_video_selector}")
elif $MP4; then
local DOWNLOAD_OPTIONS=(--merge-output-format mp4 --postprocessor-args "Merger:-threads 0 -vcodec copy -b:a ${AUDIO_BITRATE}k -ar ${AUDIO_SAMPLING_RATE} -acodec ${ENCODER} -cutoff 20000")
if [ $MAX_PIXELS -gt 1920 ] && ! $SILENT; then
echo "Maximum resolution is set to ${MAX_PIXELS}, and -m is present. Downloads will be limited to max 1080p on sites that don't provide higher resolution video streams in MP4 container (e.g. YouTube)."
fi
if $AVC; then
DOWNLOAD_OPTIONS+=(-f "(${avc_selector})+(${audio_selector})/${fallback_video_selector}")
else
DOWNLOAD_OPTIONS+=(-f "(${av1_selector})+(${audio_selector})/${fallback_video_selector}")
fi
else
local DOWNLOAD_OPTIONS=(--merge-output-format mkv -f "(${default_video_selector})+(${audio_selector})/${fallback_video_selector}")
fi
if $AUDIO_MULTISTREAMS; then
DOWNLOAD_OPTIONS+=(--audio-multistreams)
fi
DOWNLOAD_OPTIONS+=(--embed-subs --sub-langs all,-live_chat --sub-format "srt/best")
else
local DOWNLOAD_OPTIONS=(--embed-thumbnail --audio-format m4a --audio-quality ${AUDIO_BITRATE}k --postprocessor-args "ExtractAudio:-ar ${AUDIO_SAMPLING_RATE} -acodec ${ENCODER} -cutoff 20000" -x -f "${audio_selector}/best")
if $KEEP_AUDIO; then
DOWNLOAD_OPTIONS+=(-k)
fi
fi
# debug the command (non-parallel)
# a=(yt-dlp "${BASE_OPTIONS[@]}" "${DOWNLOAD_OPTIONS[@]}" "${URLS[@]}")
# echo ${a[@]}
if $PARALLEL; then
if $SILENT; then
printf "\"%s\"\n" "${URLS[@]}" | xargs -n 1 -P ${PARALLELISM} -I{} yt-dlp "${BASE_OPTIONS[@]}" "${DOWNLOAD_OPTIONS[@]}" -- "{}" > /dev/null
else
printf "\"%s\"\n" "${URLS[@]}" | xargs -n 1 -P ${PARALLELISM} -I{} yt-dlp "${BASE_OPTIONS[@]}" "${DOWNLOAD_OPTIONS[@]}" -- "{}"
fi
else
if $SILENT; then
yt-dlp "${BASE_OPTIONS[@]}" "${DOWNLOAD_OPTIONS[@]}" -- "${URLS[@]}" > /dev/null
else
yt-dlp "${BASE_OPTIONS[@]}" "${DOWNLOAD_OPTIONS[@]}" -- "${URLS[@]}"
fi
fi
}
# yt -P 1440 -v -H ESxIaJWUtAs # will download 1440p mkv
# yt -P 1440 -v -m ESxIaJWUtAs # will download 1080p mp4
# yt -P 1440 -v -m 75fEhQlc9h4 # will download 480p mp4 av01
# yt -P 1440 -v -m -M 75fEhQlc9h4 # will download 1080p mp4 avc
# yt -ka 215 ESxIaJWUtAs 75fEhQlc9h4 # two audio files simultaneously at 215kbit/s + original audio files
yt "$@"