diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..51b0640 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM whipper/whipper + +# required env variables for the script +ENV BEETS_CONFIG=/config.albums-cover.yaml +ENV LOG_DIR=/logs +ENV OUTPUT_DIR=/output + +USER root + +# setup beets +RUN pip install beets \ + && apt-get install -y python-requests \ + && mkdir /home/worker/.config/beets && chown worker: /home/worker/.config/beets +COPY beets.yml /config.albums-cover.yaml +RUN chown worker: /config.albums-cover.yaml \ + && mkdir $LOG_DIR && chown worker: $LOG_DIR +VOLUME "$LOG_DIR" + +# setup script +ENV SCRIPT_PATH=/auto-rip-audio-cd.sh +COPY auto-rip-audio-cd.sh $SCRIPT_PATH +RUN chmod +x $SCRIPT_PATH + +USER worker + +ENTRYPOINT ["/auto-rip-audio-cd.sh"] diff --git a/README.md b/README.md index 951469d..c8cc025 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,137 @@ -# Most possible unattended rip workflow +# Most Possible Unattended Rip Workflow -This is a script that allows you to quickly rip a large collection of audio cd's only with the minimum of manual intervention: __insert cd - remove cd__! +This project allows you to quickly rip a large collection of audio CD's only with the minimum of manual intervention: __insert cd - remove cd__ ## Structure -This project comprises three parts: +This project comprises two components: -1. A __shell script__: - - rip a cd with [whipper](https://github.com/JoeLametta/whipper) (fka. morituri) with a very low cpu and storage usage priority - - fall back to abcde if that fails - - automatic atgging and naming - - grab the cover art - - eject the cd +1. A shell script for the actual process: -2. (optional) A __udev rule__ to automatically run the shell script when a cd is being inserted. + - riping, tagging and naming a CD using [whipper](https://github.com/JoeLametta/whipper) + - grab the cover art using [beets](http://beets.io/) + - eject the CD -3. (optional) A __config file__ which lets you do some decision regarding the shell script. - -All steps within the script are well documented which makes it easy to customize it to any special needs. Furthermore this makes it easy for beginners to understand each step. +2. (optional) __UDEV Integration__: A UDEV rule to automatically run the shell script when a CD is being inserted. ## Installation -1. Simply download the shell script and give it the executable flag: - - `wget https://github.com/thomas-mc-work/most-possible-unattended-rip/auto-rip-audio-cd.sh && chmod +x auto-rip-audio-cd.sh` - - 1b. If you like to use have the script grabbing the cover for you then you need to install [beets](http://beets.io/) (`pip install beets`) and place a special config only for the cover grabbing process into `$HOME/.config/beets/config.albums-cover.yaml`: - - import: - copy: no - write: no - - plugins: fetchart +First you need to download and extract the project: + + curl -L https://github.com/thomas-mc-work/most-possible-unattended-rip/archive/master.tar.gz | tar xz + cd most-possible-unattended-rip-master + +### Docker + +At the moment it's required to build the whipper image yourself as the official Docker support is not yet integrated (https://github.com/JoeLametta/whipper/pull/237). + + git clone https://github.com/thomas-mc-work/whipper.git + cd whipper + git checkout -b dockerfile + docker build -t whipper/whipper . + # remove the sources again + cd .. + rm -rf whipper + +#### Build The Image + + docker build -t tmcw/mpur . + +#### Run The Container + + [ ! -d config ] && mkdir config + [ ! -d logs ] && mkdir logs + [ ! -d output ] && mkdir output + + docker run --rm \ + --device=/dev/cdrom \ + -v "${PWD}/config":/home/worker/.config/whipper \ + -v "${PWD}/logs":/logs \ + -v "${PWD}/output":/output \ + tmcw/mpur + +### Native + + # Mark the script executable + chmod +x auto-rip-audio-cd.sh + # Install beets via pip + pip install --user beets + # Add the beets configuration + curl -Lo "${HOME}/.config/beets/config.albums-cover.yaml https://raw.githubusercontent.com/thomas-mc-work/most-possible-unattended-rip/master/beets.yml + +Now you can simply execute the script after inserting th audio CD: + + ./auto-rip-audio-cd.sh -2. (optional) Add a udev rule to automatically trigger the script: +### UDEV Integration - `echo "SUBSYSTEM==\"block\", SUBSYSTEMS==\"scsi\", KERNEL==\"sr?\", ENV{ID_TYPE}==\"cd\", ENV{ID_CDROM}==\"?*\", ENV{ID_CDROM_MEDIA_TRACK_COUNT_AUDIO}==\"?*\", ACTION==\"change\", RUN+=\"/bin/su -lc '/auto-rip-audio-cd.sh' \"" | sudo tee 80-audio-cd.rules` +Add a UDEV rule to automatically trigger the script: - It's important here to replace `` and `` with the appropriate values. The script will be run with the according user permissions. This is important to get the correct locale settings in the environment. + echo "SUBSYSTEM==\"block\", SUBSYSTEMS==\"scsi\", KERNEL==\"sr?\", ENV{ID_TYPE}==\"cd\", ENV{ID_CDROM}==\"?*\", ENV{ID_CDROM_MEDIA_TRACK_COUNT_AUDIO}==\"?*\", ACTION==\"change\", RUN+=\"/bin/su -lc '/auto-rip-audio-cd.sh' \"" | sudo tee 80-audio-cd.rules -3. (optional) Download the config file template into your profiles config (`$HOME/.config`) folder: +Just be sure to substitude the placeholders `` and `` by the appropriate values. The script will be run with the according user permissions. This is important to get the correct locale settings in the environment. - `wget https://github.com/thomas-mc-work/most-possible-unattended-rip/auto-rip.cfg -O $HOME/.config/auto-rip.cfg` +**Config File To Control The UDEV Automatism** - These options are available: +Create a config file in your profiles config (`$HOME/.config`) folder: + +If you want to use a config file then you're required to create an intermediate shell script that is invoked by the UDEV rule: + + #!/usr/bin/env bash + + # end previous shutdown if one is active + sudo shutdown -c + + # marker file for skipping the automated ripping + CONFIG_FILE="${HOME}/.config/auto-rip.cfg" + + # include config file + if [ -f "$CONFIG_FILE" ]; then + . "$CONFIG_FILE" + fi + + # optionally omit the process by config setting + if [ "$DISABLED" = 1 ]; then + echo "# omitting auto rip due to config setting" + exit 0 + fi + + nice -n 19 ionice -c 3 /path/to/auto-rip-audio-cd.sh + + # reread the config file to include a late shutdown decision + if [ -f "$CONFIG_FILE" ]; then + . "$CONFIG_FILE" + fi + + # optionally shutdown after a short delay + if [ "$SHUTDOWN" = 1 ]; then + echo "# shutting down the system" + if ! [[ $SHUTDOWN_TIMEOUT =~ ^[0-9]+$ ]]; then SHUTDOWN_TIMEOUT=3; fi + sudo shutdown -h $SHUTDOWN_TIMEOUT + fi + + +The config file: + + # disable auto ripping? + DISABLED=0 + # shutdown after finish? + SHUTDOWN=0 + # shutdown timeout in minutes + SHUTDOWN_TIMEOUT=3 + +These options are available: - `DISABLED={0,1}`: Disable the script. Good if you like listen to some music CDs without instantly ripping them - `SHUTDOWN={0,1}`: You can choose whether to automatically shutdown the system ofter the rip process has finished. This is good e.g. when going to bed and letting the system finish the last CD by itself. For using this you need to have the permission to shutdown the system via the command line. You can achieve this by inserting `%sudo ALL = NOPASSWD: /sbin/shutdown` into `/etc/sudoers`. - - `SHUTDOWN_TIMEOUT=3`: Lets you define the shutdown timeout + - `SHUTDOWN_TIMEOUT=`: Lets you define the shutdown timeout ## Links - [whipper](https://github.com/JoeLametta/whipper) - [good explanations and debug help with udev on arch wiki](https://wiki.archlinux.org/index.php/udev) - [beets](http://beets.io/) -- [abcde best of breed configuration](http://www.andrews-corner.org/linux/abcde/index.html) -- [let abcde grab album art](http://www.andrews-corner.org/linux/abcde/getalbumart.html) -Any comments, questions or PRs are very welcome! \ No newline at end of file +--- + +**Any comments, questions or PRs are very welcome!** \ No newline at end of file diff --git a/auto-rip-audio-cd.sh b/auto-rip-audio-cd.sh index 247ddab..ac33aab 100755 --- a/auto-rip-audio-cd.sh +++ b/auto-rip-audio-cd.sh @@ -1,91 +1,53 @@ #!/usr/bin/env bash -# automatisches rippen einer audio-cd -# https://github.com/JoeLametta/whipper -# Vorbereitung morituri: -# # rip offset find -# # rip drive analyze +# Automated rip process of an audio CD. +set -u -LOG_DIR="$HOME/logs/audio-rip" -OUTPUT_DIR="$HOME/rip" +log_dir=${LOG_DIR:-"$HOME/logs/audio-rip"} +output_dir=${OUTPUT_DIR:-"$HOME/rip"} +beets_config=${BEETS_CONFIG:-"$HOME/.config/beets/config.albums-cover.yaml"} -# ============ +log_file="${log_dir}/rip-$(date +%Y-%m-%dT%H-%M-%S).log" -LOG_FILE="$LOG_DIR/rip-$(date +%Y-%m-%dT%H-%M-%S).log" +fn_log () { + echo "### $*" | tee -a "$log_file" +} -# end previous shutdown if one is active -sudo shutdown -c - -# marker file for skipping the automated ripping -CONFIG_FILE="${HOME}/.config/auto-rip.cfg" - -# include config file -if [ -f "$CONFIG_FILE" ]; then - . "$CONFIG_FILE" -fi - -# optionally omit the process by config setting -if [ "$DISABLED" = 1 ]; then - echo "# omitting auto rip due to config setting" | tee -a "$LOG_FILE" - exit 0 -fi - -echo "# auto rip started" | tee -a "$LOG_FILE" +fn_log "auto rip started" # PYTHONIOENCODING: workaround for an issue: https://github.com/JoeLametta/whipper/issues/43 -nice -n 19 ionice -c 3 whipper cd rip --output-directory="$OUTPUT_DIR" -U true >>"$LOG_FILE" 2>&1 -SC=$? -echo "# auto rip finished" | tee -a "$LOG_FILE" +export PYTHONIOENCODING="utf-8" +whipper cd rip --output-directory="$output_dir" -U >> "$log_file" 2>&1 +sc_whipper=$? # grab the cover art -if [ $SC == 0 ]; then +if [ $sc_whipper == 0 ]; then + fn_log "whipper rip finished" + # replace the carriage returns with proper line breaks and search for the output pattern - FOLDER_LINE=$(tr '\015' "\n" < "${LOG_FILE}" | grep "utput directory") - echo "# folder line: $FOLDER_LINE" >> "${LOG_FILE}" + folder_line=$(tr '\015' "\n" < "$log_file" | grep "utput directory") - if [ "$FOLDER_LINE" == "" ]; then - echo "# result: success (but couldn't find output folder for fetching cover art)" | tee -a "$LOG_FILE" + if [ -z "$folder_line" ]; then + fn_log "result: success (but couldn't find output folder for fetching cover art)" else # remove the search pattern - FOLDER=${FOLDER_LINE/Creating output directory /} - echo "# output path: $FOLDER" >> "${LOG_FILE}" + output_path=${folder_line/Creating output directory /} + fn_log "output path: $output_path" - # if you have beets then grab image with beets - # can be removed once this is closed: https://github.com/JoeLametta/whipper/issues/50 + # Use beets for image grabbing if it is available. + # Can be removed once this is implemented: https://github.com/JoeLametta/whipper/issues/50 if type beet >/dev/null 2>&1; then # -l: discard all additions to the library # -c: path to the config file # import: normally you import the file here and grab the cover alongside # -q: quiet - don't ask for user input. Either it works or forget about it - beet -l /dev/null -c "$HOME/.config/beets/config.albums-cover.yaml" import -q "$FOLDER" >> "${LOG_FILE}" - echo "# result: success" | tee -a "$LOG_FILE" + echo a | beet -c "$beets_config" import "$output_path" >> "$log_file" + sc_beets=$? + fn_log "beet result: ${sc_beets}" + exit $sc_beets fi fi else - echo "# result: no success with whipper/morituri: status code = ${SC}" | tee -a "$LOG_FILE" - - # if you have abcde then use it as fallback - if type abcde >/dev/null 2>&1; then - echo "# trying abcde" | tee -a "$LOG_FILE" - abcde | tee -a "$LOG_FILE" - SC=$? - - if [ $SC == 0 ]; then - echo "# result: success with abcde" | tee -a "$LOG_FILE" - eject - else - echo "# result: no success with abcde: status code = ${SC}" | tee -a "$LOG_FILE" - fi - fi + fn_log "failed to rip: ${sc_whipper}" + exit $sc_whipper fi -# reread the config file to include a late shutdown decision -if [ -f "$CONFIG_FILE" ]; then - . "$CONFIG_FILE" -fi - -# optionally shutdown after a short delay -if [ "$SHUTDOWN" = 1 ]; then - echo "# shutting down the system" | tee -a "$LOG_FILE" - if ! [[ $SHUTDOWN_TIMEOUT =~ ^[0-9]+$ ]]; then SHUTDOWN_TIMEOUT=3; fi - sudo shutdown -h $SHUTDOWN_TIMEOUT | tee -a "$LOG_FILE" -fi diff --git a/auto-rip.cfg b/auto-rip.cfg deleted file mode 100644 index e7927cb..0000000 --- a/auto-rip.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# disable auto ripping? -DISABLED=0 -# shutdown after finish? -SHUTDOWN=0 -# shutdown timeout in minutes -SHUTDOWN_TIMEOUT=3 diff --git a/beets.yml b/beets.yml new file mode 100644 index 0000000..67e9b22 --- /dev/null +++ b/beets.yml @@ -0,0 +1,7 @@ +library: /dev/null + +import: + copy: no + write: no + +plugins: fetchart \ No newline at end of file diff --git a/docker-build-image.sh b/docker-build-image.sh new file mode 100755 index 0000000..1304878 --- /dev/null +++ b/docker-build-image.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errexit ## set -e : exit the script if any statement returns a non-true return value + +docker build -t tmcw/mpur:0.6-1 -t tmcw/mpur:latest .