#!/bin/bash begin=$(date +"%s") # saner programming env: these switches turn some bugs into errors set -o errexit -o pipefail -o noclobber -o nounset source="-" dest="-" verbose=2 help=false temp="/tmp/converted" CheckDeps() { # Check getopt ! getopt --test > /dev/null if [[ ${PIPESTATUS[0]} -ne 4 ]]; then VerboseOutput 5 "\`getopt --test\` failed" VerboseOutput 5 "Sorry, It seems that your shell is not supported" VerboseOutput 5 "If you're using MacOS or another unix-like system, please install GNU getopt" ExecTime exit 1 fi # Check lame if [[ ! $(lame --version 2>/dev/null) ]]; then VerboseOutput 5 "\`lame --version\` failed" VerboseOutput 5 "Sorry, It seems that lame is not installed on your system" VerboseOutput 5 "Please install lame from your repositories and make sure it is available in your \$PATH" ExecTime exit 1 fi } GetOptions() { # https://stackoverflow.com/a/29754866 OPTIONS=s:d:t:v::h LONGOPTS=source:,dest:,temp:,verbose::,help # -use ! and PIPESTATUS to get exit code with errexit set # -temporarily store output to be able to check for errors # -activate quoting/enhanced mode (e.g. by writing out “--options”) # -pass arguments only via -- "$@" to separate them correctly ! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") if [[ ${PIPESTATUS[0]} -ne 0 ]]; then # e.g. return value is 1 # then getopt has complained about wrong arguments to stdout Usage exit 2 fi # read getopt’s output this way to handle the quoting right: eval set -- "$PARSED" # now enjoy the options in order and nicely split until we see -- while true; do case "$1" in -v|--verbose) verbose=2 if [[ $2 != "" ]]; then verbose=${2} shift fi shift ;; -h|--help) help=true shift ;; -s|--source) source="$2" shift 2 ;; -d|--dest) dest="$2" shift 2 ;; -t|--temp) temp="$2" shift 2 ;; --) shift break ;; *) VerboseOutput 5 "Programming error" return 3 ;; esac done if [[ ! -z ${1+x} ]]; then source=$1 fi if [[ ! -z ${2+x} ]]; then dest=$2 fi if [[ $dest == "-" ]] || [[ $source == "-" ]]; then help=true fi } Usage() { echo "" echo "Usage:" echo "music-sync -s|--source -d|--dest " echo "music-sync " echo "" echo "Syncronises music from one folder to another." echo "" echo "Options:" echo " -s, --source The source folder of the music" echo " -d, --dest The destionation folder of the music" echo " -t, --temp The temporary cache for converted files (default: /tmp/converted)" echo " -v, --verbose <0-6> Set log level (default: 2)" echo " -h, --help Display this help text" echo "" echo "Log levels:" echo " 0 | Verbose" echo " 1 | Debug" echo " 2 | Info" echo " 3 | Warning" echo " 4 | Error" echo " 5 | Fatal" echo " 6 | Silence" echo "" echo "Exit Codes:" echo " 1 Dependencies not met" echo " 2 Invalid Argument" echo " 3 Source Unreachable" echo " 4 Destination Unreachable" echo " 5 Command failed" echo "" } VerboseOutput() { level="" if [[ $verbose -le $1 ]]; then case "$1" in 0) level="\033[1;36mVerbose\033[0m" ;; 1) level="\033[1;34m Debug \033[0m" ;; 2) level="\033[1;37m Info \033[0m" ;; 3) level="\033[1;33mWarning\033[0m" ;; 4) level="\033[1;31m Error \033[0m" ;; 5) level="\033[1;30m Fatal \033[0m" ;; esac echo -e "[$level] $2" >&2 fi } CreateFileList() { # ${1} /mnt/hdd/Example-Artist/Example-Album # ${2} /mnt/mtp/Example-Artist/Example-Album # ${3} Example-Artist/Example-Album IFS="" sourcepath="${1/\[/\\\[}/*" sourcepath="${sourcepath/\]/\\\]}" for file in $sourcepath; do relfile="${file#"$1/"}" if [[ -d "${1}/$relfile" ]]; then newdir="${3}/$relfile" newdir=${newdir#"/"} VerboseOutput 1 "Entering $newdir" CreateFileList "${1}/$relfile" "${2}/$relfile" "$newdir" elif [[ ! -f "${2}/$relfile" || "${1}/$relfile" -nt "${2}/$relfile" ]]; then echo ${3}/$relfile >> /tmp/music-sync-filelist VerboseOutput 2 "Added: ${3}/${relfile}" fi done } ConvertFiles() { curline=0 percentage=0 while read -r line do if [[ ! -f "${source}/$line" ]]; then VerboseOutput 5 "Source-file ${source}/$line Unreachable" ExecTime exit 3 fi curline=$(expr ${curline} + 1) total=$(cat /tmp/music-sync-filelist | wc -l) percentage=$(echo "scale=4;${curline}/${total}" | bc) percentage=$(echo "scale=2;${percentage}*100" | bc) VerboseOutput 1 "Converting: $line" VerboseOutput 2 "Progress: $curline / $total (${percentage%00}%) Step 1 of 2" if [[ "$temp/$line" = */* ]]; then mkdir -p "$temp/${line%/*}"; fi; if [[ ! -f "$temp/$line" || "${source}/$line" -nt "$temp/$line" ]]; then lame -b 192 $source/$line $temp/$line 1>/dev/null 2>/dev/null VerboseOutput 2 "Converted: $line" else VerboseOutput 3 "$line already converted" fi; done < "/tmp/music-sync-filelist" } CopyFiles() { curline=0 percentage=0 while read -r line do if [[ ! -d "$dest" ]]; then VerboseOutput 5 "Destination unreachable" ExecTime exit 4 fi if [[ ! -f "${temp}/$line" ]]; then VerboseOutput 5 "Source-file ${temp}/$line Unreachable" ExecTime exit 3 fi curline=$(expr ${curline} + 1) total=$(cat /tmp/music-sync-filelist | wc -l) percentage=$(echo "scale=4;${curline}/${total}" | bc) percentage=$(echo "scale=2;${percentage}*100" | bc) VerboseOutput 1 "Copying: $line" VerboseOutput 2 "Progress: $curline / $total (${percentage%00}%) Step 2 of 2" if [[ "$dest/$line" = */* ]]; then mkdir -p "$dest/${line%/*}"; fi; cp -f $temp/$line $dest/$line 1>/dev/null 2>/dev/null VerboseOutput 2 "Copied: $line" done < "/tmp/music-sync-filelist" } CleanUp() { VerboseOutput 1 "Cleaning Up" if [[ -f /tmp/music-sync-filelist ]]; then rm "/tmp/music-sync-filelist" fi VerboseOutput 1 "Done" } ExecTime() { termin=$(date +"%s") difftimelps=$(($termin-$begin)) VerboseOutput 1 "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution." } ErrorHandler() { VerboseOutput 5 "Error while executing $1" CleanUp exit 5 } ExitHandler() { VerboseOutput 5 "Aborted" CleanUp exit 0 } trap 'ExitHandler' SIGINT trap 'ErrorHandler $BASH_COMMAND' ERR CheckDeps GetOptions $@ if [[ $help == true ]]; then Usage exit fi if [[ -f /tmp/music-sync-filelist ]]; then rm /tmp/music-sync-filelist fi CreateFileList $source $dest "" if [[ ! -f /tmp/music-sync-filelist ]]; then VerboseOutput 2 "Nothing to do!" CleanUp exit 0 fi ConvertFiles CopyFiles CleanUp ExecTime