<p>
Once again, the command line is the root of all that is good in the world. This time, it has helped improve on a long-standing issue for me: what is the easiest way to get a copy of all the <a href='http://www.youtube.com/playlist?list=PLmku2swCXQpqWAZSscjV4h9bcLennVcif' target='_blank'>luscious melodies</a> i hear on youtube? Courtesy of <a href='http://rg3.github.io/youtube-dl/' target='_blank'>youtube-dl</a>, a nifty little command line utility, this problem has been solved. However, every once in awhile it throws errors and i wanted a wrapper bash script to take care of this and some other processing. I'll briefly go over the code.
</p>
Once again, the command line is the root of all that is good in the world. This time, it has helped improve on a long-standing issue for me: what is the easiest way to get a copy of all the luscious melodies i hear on youtube? Courtesy of youtube-dl, a nifty little command line utility, this problem has been solved. However, every once in awhile it throws errors and i wanted a wrapper bash script to take care of this and some other processing. I'll briefly go over the script (scroll to end of article to see full code).
The script solves three basic problems: re-running youtube-dl after errors, automatically deciding whether input is a list of links or a single URL, and formatting the output files. Formatting the music output files was the easiest: a base command was stored in a variable (baseCmd) that contains formatting for the title and extension (%(title)s.%(ext)s) as per the youtube-dl documentation.
Automatically choosing to run youtube-dl against a single or multiple URLs involved detecting http:// in the input and modifying the baseCmd variable from there. I avoided using getopts this time around to parse input arguments as the script was supposed to be simple as possible; i'll add this standardized input style in future revisions.
Handling errors involved taking advantage of the tee command. The main reason for using tee was to show the user output while also writing all the output from the shell to a file. After the command had finished running, successfully or not, I then grep the output, checking for ERROR, which youtube-dl outputs on error. The double passing of tee allows me to capture both STDOUT and STDERR, else just STDOUT would never capture the thrown errors and the program would exit even when problems arise. The script then calls itself passing the original argument (the base case for the recursion is the lack of an error...).
This should prove useful for people who want a little script to automate some parts of using this incredibly useful tool.
youtube.sh wrapper script
- # /bin/bash
- # biafra ahanonu
- # updated: 2013.05.05
- # youtube-dl wrapper script to help handle errors and automate some things
- printHelp(){
- echo "youtube.sh FILE ARG
- ARG can be:
- -h for help
- -a for list of youtube URLs in a file, use only if filename contains 'http://'
- empty for single URL or file (auto-detects)"
- exit 0
- }
- # help string
- helpString='-h -help --h --help'
- # check if first argument contains a URL
- URLcheck=$(echo $1 | grep 'http://' | wc -l)
- # base command to use
- baseCmd="youtube-dl -ixko %(title)s.%(ext)s --no-post-overwrites --verbose --restrict-filenames --audio-format m4a -R 30 "
- # check if first argument is a help string
- if [[ "$helpString" == *"$1"* ]]; then
- printHelp
- fi
- # check if second arg is empty
- if [ -z "$2" ]; then
- # If contains URL, do normally
- if [ $URLcheck -eq 0 ]; then
- cmd=$baseCmd"-a $1"
- # Enter batch mode if first arg is not URL
- elif [ ! $URLcheck -eq 0 ]; then
- cmd=$baseCmd"$1"
- fi
- # User should flag with -a
- elif [ $2 == -a ]; then
- cmd=$baseCmd"-a $1"
- fi
- echo $cmd
- # continue until no error encountered
- error=1
- while [ $error -eq 1 ]; do
- # clear output file
- echo '' > youtube.output
- # save to file and output to cmd line, captures STDOUT and STDERR to allow restart of script if errors
- # $cmd | tee -ai youtube.output
- $cmd > >(tee -ai youtube.output) 2> >(tee -ai youtube.output >&2)
- # check if an error occurred, if so, restart script to resume download
- if [ $(grep 'ERROR' youtube.output | wc -l) -eq 0 ]; then
- echo No errors found, exiting...
- error=0
- else
- echo ________________________________________________________
- echo Errors found, restarting script...
- bash youtube.sh $1
- fi
- done