-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathyt2ab.sh
189 lines (147 loc) · 6.35 KB
/
yt2ab.sh
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
#!/bin/bash
# Uncomment for verbose Bash logging
set +x
# Stop on all errors
set -e
# Ensure proper exit on SIGINT and SIGTERM
trap "exit 0" SIGINT SIGTERM
###############################################################################
# Default Arguments #
###############################################################################
# Static definitions; purpose of each variable as indicated by its name
# Set the default audio speed to 1 (leaving the audio unchanged)
DEFAULT_AUDIO_SPEED=1
# Set the default audio quality to 4; see <https://trac.ffmpeg.org/wiki/Encode/MP3> for details
DEFAULT_AUDIO_QUALITY=4
# Set the default random time (in seconds) to wait between successive downloads;
# YouTube can quickly block IPs if they request too many downloads in a short span of time
let RANDOM_WAIT_TIME=5*60
###############################################################################
# Helper Functions #
###############################################################################
# Helper function for checking multiple dependencies
function check_dependencies {
dependencies=$1
for dependency in ${dependencies};
do
if ! hash ${dependency} 2>/dev/null;
then
echo -e "This script requires '${dependency}' but it cannot be detected. Aborting..."
exit 1
fi
done
}
# Helper function for displaying help text (pun intended)
function display_help {
script_name=`basename "$0"`
echo "Usage : $script_name -u <youtube-url> -s [audio-speed] -q [audio-quality] -v(erbose)"
echo "Example: $script_name -u https://youtu.be/WRbalzuvms4"
echo "Example: $script_name -u https://youtu.be/WRbalzuvms4 -s 1.5"
echo "Example: $script_name -u https://youtu.be/WRbalzuvms4 -s 1.5 -q 3"
echo "Example: $script_name -u https://youtu.be/WRbalzuvms4 -s 1.5 -q 3 -v"
echo "Note: for audio quality see also column 'ffmpeg option' on <https://trac.ffmpeg.org/wiki/Encode/MP3>; defaults to 4"
}
# Helper function for echoing debug information
function echo_debug {
if $is_verbose;
then
echo "$1"
fi
}
# Helper function for downloading and converting YouTube videos from a given URL
function download_and_convert_youtube_video () {
_url=$1
# Dynamic file name definitions
_youtube_filename=$(youtube-dl --get-filename "${_url}")
_cover_jpeg="${_youtube_filename}".jpg
_cover="${_cover_jpeg}"
_cover_webp="${_youtube_filename}".webp
_youtube_audio="${_youtube_filename}"-youtube-audio.mp3
_audiobook_filename="${_youtube_filename}.mp3"
# Ensure proper cleanup upon function return
trap 'rm -f "${_cover_jpeg}"; rm -f "${_cover_webp}"; rm -f "${_youtube_audio}"' RETURN
# Download thumbnail and audio from given YouTube ${_url}
youtube-dl "${_url}" --write-thumbnail --skip-download -o "${_cover}"
youtube-dl "${_url}" -x --audio-format mp3 --audio-quality 0 -o "${_youtube_audio}"
# Handle special case of the cover not being in standard (read: expected) JPEG format
if [ ! -f "${_cover}" ]
then
if [ -f "${_cover_webp}" ]
then
_cover="${_cover_webp}"
else
echo "The downloaded cover is neither in JPEG nor in WebP format. Aborting..."
exit 1
fi
fi
# Convert audio to Mp3, including thumbnail for easier recognition
ffmpeg -i "${_youtube_audio}" -i "${_cover}" -map_metadata 0 -map 0 -map 1 -codec:a libmp3lame -qscale:a ${audio_quality} -filter:a "atempo=${audio_speed}" "${_audiobook_filename}"
# Remove original extension from ${_audiobook_filename}
_base=${_audiobook_filename%.*.mp3}
mv "${_audiobook_filename}" "${_base}.mp3"
}
###############################################################################
# Dependency Checks #
###############################################################################
# Check dependencies of Bash script
DEPENDENCIES='youtube-dl ffmpeg jq'
check_dependencies "${DEPENDENCIES}"
###############################################################################
# Input Argument Handling #
###############################################################################
# Input argument parsing
while getopts ":hu:s:q:v" option
do
case "${option}" in
u) url=${OPTARG};;
s) audio_speed=${OPTARG};;
q) audio_quality=$OPTARG;;
v) is_verbose=true;;
h) display_help; exit -1;;
:) echo "Missing option argument for -$OPTARG" >&2; exit 1;;
esac
done
# Mandatory input arguments check
if [[ -z "${url}" ]]; then
display_help
exit -1
fi
# Handle optional verbosity parameter
is_verbose=${is_verbose:-false}
# Handle optional audio speed parameter
audio_speed=${audio_speed:-$DEFAULT_AUDIO_SPEED}
echo_debug "Audio speed set to ${audio_speed}"
# Handle optional audio speed parameter
audio_quality=${audio_quality:-$DEFAULT_AUDIO_QUALITY}
echo_debug "Audio quality set to ${audio_quality}"
###############################################################################
# YouTube Audio Download #
###############################################################################
# Retrieve the flat JSON representation of the "${url}" playlist (using the fact
# that 'youtube-dl' produces the identical JSON format for both single videos
# as well as playlists) and store each result in an array in order to avoid
# interferences with later outputs by other programmes
declare -a video_jsons
while IFS= read -r video_json
do
video_jsons+=("${video_json}")
done < <(youtube-dl -j --flat-playlist "${url}")
# Download and convert each video in "${video_jsons}"; inspired by <https://superuser.com/a/1359132>
for video_json in "${video_jsons[@]}"
do
title=$(jq -r '.title' <<< "${video_json}")
id=$(jq -r '.id' <<< "${video_json}")
video_url="https://youtu.be/${id}"
echo "Processing ${video_url}: '${title}'..."
download_and_convert_youtube_video "${video_url}"
echo "...done processing '${title}'!"
# Sleep random amount of seconds in case multiple videos are to be downloaded;
# see also <https://github.com/ytdl-org/youtube-dl/issues/22641> for details
if [ ${#video_jsons[@]} -gt 1 ]
then
_sleep_time=$((RANDOM % RANDOM_WAIT_TIME + 1))
echo "Sleeping ${_sleep_time} seconds between successive requests..."
sleep ${_sleep_time}
echo "...done sleeping!"
fi
done