Merge branch 'develop'

This commit is contained in:
Jeroen De Meerleer 2019-03-16 17:52:21 +01:00
commit 962748da1e
2 changed files with 277 additions and 55 deletions

View File

@ -1,5 +1,5 @@
# Bash Music sync # Bash Music sync
(c) 2018 Jeroen De Meerleer <me@jeroened.be> (c) 2018-2019 Jeroen De Meerleer <me@jeroened.be>
Script to sync music from one folder to another Script to sync music from one folder to another
@ -12,14 +12,34 @@ music-sync <options> <source> <destination>
Syncronises music from one folder to another. Syncronises music from one folder to another.
Options: Options:
-s, --source <source> The source folder of the music -s, --source <source> The source folder of the music
-d, --dest <destination> The destionation folder of the music -d, --dest <destination> The destination folder of the music
-v, --verbose Enable verbose output -t, --temp <folder> The temporary cache for converted files (default: /tmp/converted)
-h, --help Display this help text -c, --convert <bitrate> Convert files to a given bitrate in kbps before syncing (default: 192)
-a, --resize-art <width> Resize album-art before syncing (default width: 200)
-v, --verbose <0-6> Set log level (default: 2)
-h, --help Display this help text
Log levels:
0 | Verbose
1 | Debug
2 | Info
3 | Warning
4 | Error
5 | Fatal
6 | Silence
Exit Codes:
1 Dependencies not met
2 Invalid Argument
3 Source Unreachable
4 Destination Unreachable
5 Command failed
``` ```
## Licence ## Licence
Copyright 2018 Jeroen De Meerleer <me@jeroened.be> Copyright 2018-2019 Jeroen De Meerleer <me@jeroened.be>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -1,29 +1,67 @@
#!/bin/bash #!/bin/bash
begin=$(date +"%s")
# saner programming env: these switches turn some bugs into errors # saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset set -o errexit -o pipefail -o noclobber -o nounset
source="-" source="-"
dest="-" dest="-"
verbose=true convert=false
verbose=2
bitrate=192
help=false help=false
temp="/tmp/converted"
convertart=false
coverartsize=200
CheckDeps() {
if [[ $1 == 0 ]]; then
# 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
fi
if [[ $1 == 1 ]]; then
# Check lame
if [[ $convert == true && ! $(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"
VerboseOutput 5 "Otherwise disable conversion"
ExecTime
exit 1
fi
# Check EyeD3
if [[ $convertart == true && ! $(eyeD3 --version 2>/dev/null) ]]; then
VerboseOutput 5 "\`eyeD3 --version\` failed"
VerboseOutput 5 "Sorry, It seems that eyeD3 is not installed on your system"
VerboseOutput 5 "Please install eyeD3 from your repositories and make sure it is available in your \$PATH"
VerboseOutput 5 "Otherwise disable albumart conversion"
ExecTime
exit 1
fi
# Check ImageMagick
if [[ $convertart == true && ! $(convert --version 2>/dev/null) ]]; then
VerboseOutput 5 "\`convert --version\` failed"
VerboseOutput 5 "Sorry, It seems that ImageMagick is not installed on your system"
VerboseOutput 5 "Please install ImageMagick from your repositories and make sure it is available in your \$PATH"
VerboseOutput 5 "Otherwise disable albumart conversion"
ExecTime
exit 1
fi
fi
}
GetOptions() { GetOptions() {
# https://stackoverflow.com/a/29754866 # https://stackoverflow.com/a/29754866
! getopt --test > /dev/null OPTIONS=s:d:t:c::a::v::h
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then LONGOPTS=source:,dest:,temp:,convert::,resize-art::,verbose::,help
VerboseOutput "Fatal" "\`getopt --test\` failed"
echo "Sorry, It seems that your shell is not supported"
echo "If you're using mac or another unix-like system, please install GNU getopt"
exit 1
fi
# Parsing style without -s or -d
source=${*: -2:1}
dest=${*: -1:1}
OPTIONS=s:d:vh
LONGOPTS=source:,dest:,verbose,help
# -use ! and PIPESTATUS to get exit code with errexit set # -use ! and PIPESTATUS to get exit code with errexit set
# -temporarily store output to be able to check for errors # -temporarily store output to be able to check for errors
@ -40,12 +78,15 @@ GetOptions() {
# read getopts output this way to handle the quoting right: # read getopts output this way to handle the quoting right:
eval set -- "$PARSED" eval set -- "$PARSED"
verbose=false
# now enjoy the options in order and nicely split until we see -- # now enjoy the options in order and nicely split until we see --
while true; do while true; do
case "$1" in case "$1" in
-v|--verbose) -v|--verbose)
verbose=true verbose=2
if [[ $2 != "" ]]; then
verbose=${2}
shift
fi
shift shift
;; ;;
-h|--help) -h|--help)
@ -60,18 +101,48 @@ GetOptions() {
dest="$2" dest="$2"
shift 2 shift 2
;; ;;
-t|--temp)
temp="$2"
shift 2
;;
-c|--convert)
convert=true
bitrate=192
if [[ $2 != "" ]]; then
bitrate=${2}
shift
fi
shift
;;
-a|--resize-art)
convertart=true
coverartsize=200
if [[ $2 != "" ]]; then
coverartsize=${2}
shift
fi
shift
;;
--) --)
shift shift
break break
;; ;;
*) *)
echo "Programming error" VerboseOutput 5 "Programming error"
return 3 return 3
;; ;;
esac esac
done done
if [[ $dest == "" ]] || [[ $source == "" ]]; then if [[ ! -z "${1+x}" ]]; then
source="$1"
fi
if [[ ! -z "${2+x}" ]]; then
dest="$2"
fi
if [[ $dest == "-" ]] || [[ $source == "-" ]]; then
help=true help=true
fi fi
} }
@ -85,81 +156,212 @@ Usage() {
echo "Syncronises music from one folder to another." echo "Syncronises music from one folder to another."
echo "" echo ""
echo "Options:" echo "Options:"
echo " -s, --source <source> The source folder of the music" echo " -s, --source <source> The source folder of the music"
echo " -d, --dest <destination> The destionation folder of the music" echo " -d, --dest <destination> The destination folder of the music"
echo " -v, --verbose Enable verbose output" echo " -t, --temp <folder> The temporary cache for converted files (default: /tmp/converted)"
echo " -h, --help Display this help text" echo " -c, --convert <bitrate> Convert files to a given bitrate in kbps before syncing (default: 192)"
echo " -a, --resize-art <width> Resize album-art before syncing (default width: 200)"
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 "" echo ""
} }
VerboseOutput() { VerboseOutput() {
if [[ "$verbose" = true ]]; then level=""
echo "[$1] $2" >&2 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 fi
} }
CreateFileList() { CreateFileList() {
# ${1} /mnt/hdd/Example-Artist/Example-Album # ${1} /mnt/hdd/Example-Artist/Example-Album
# ${2} /mnt/mtp/Example-Artist/Example-Album # ${2} /mnt/mtp/Example-Artist/Example-Album
# ${3} Example-Artist/Example-Album/ # ${3} Example-Artist/Example-Album
IFS="" IFS=""
sourcepath="${1}/*" sourcepath="${1/\[/\\\[}/*"
sourcepath="${sourcepath/\]/\\\]}"
for file in $sourcepath; do for file in $sourcepath; do
relfile="${file#"$1/"}" origfile="${file#"$1/"}"
if [[ -d "${1}/$relfile" ]]; then relfile=$(echo ${origfile} | sed -e 's/\(\.\)*$//g')
if [[ -d "${1}/$origfile" ]]; then
newdir="${3}/$relfile" newdir="${3}/$relfile"
newdir=${newdir#"/"} newdir=${newdir#"/"}
VerboseOutput "Info" "Entering $newdir" VerboseOutput 1 "Entering $newdir"
CreateFileList "${1}/$relfile" "${2}/$relfile" "$newdir" CreateFileList "${1}/$origfile" "${2}/$relfile" "$newdir"
elif [[ ! -f "${2}/$relfile" || "${1}/$relfile" -nt "${2}/$relfile" ]]; then elif [[ "${1}/$origfile" != *".m3u" ]] && [[ ! -f "${2}/$relfile" || "${1}/$origfile" -nt "${2}/$relfile" ]]; then
echo ${3}/$relfile >> /tmp/music-sync-filelist echo ${3}/$relfile >> /tmp/music-sync-filelist
VerboseOutput "Info" "Added: ${3}/${relfile}" VerboseOutput 2 "Added: ${3}/${relfile}"
fi fi
done done
} }
ConvertFiles() { ConvertFiles() {
mkdir -p /tmp/converted curline=0
percentage=0
while read -r line while read -r line
do do
VerboseOutput "Info" "Converting $line"
if [[ "/tmp/converted/$line" = */* ]]; then if [[ ! -f "${source}/$line" ]]; then
mkdir -p "/tmp/converted/${line%/*}"; 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; fi;
lame -b 192 $source/$line /tmp/converted/$line 1>/dev/null 2>/dev/null if [[ ! -f "$temp/$line" || "${source}/$line" -nt "$temp/$line" ]]; then
VerboseOutput "Info" "Converted $line" lame -b ${bitrate} $source/$line $temp/$line 1>/dev/null 2>/dev/null
VerboseOutput 2 "Converted: $line"
if [[ $convertart == true ]]; then
mkdir -p "$temp/$line-images/"
eyeD3 --write-images "$temp/$line-images/" "$temp/$line" 1>/dev/null 2>/dev/null
convert "$temp/$line-images/FRONT_COVER.*" -resize ${coverartsize}x${coverartsize} "$temp/$line-images/FRONT_COVER.jpg" 1>/dev/null 2>/dev/null
eyeD3 --remove-all-images "$temp/$line" 1>/dev/null 2>/dev/null
eyeD3 --add-image "$temp/$line-images/FRONT_COVER.jpg:FRONT_COVER" "$temp/$line" 1>/dev/null 2>/dev/null
VerboseOutput 2 "Converted cover art: $line"
fi
else
VerboseOutput 3 "$line already converted"
fi;
done < "/tmp/music-sync-filelist" done < "/tmp/music-sync-filelist"
} }
CopyFiles() { CopyFiles() {
curline=0
percentage=0
while read -r line while read -r line
do do
VerboseOutput "Info" "Copy $line" 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 if [[ "$dest/$line" = */* ]]; then
mkdir -p "$dest/${line%/*}"; mkdir -p "$dest/${line%/*}";
fi; fi;
cp -f $source/$line $dest/$line 1>/dev/null 2>/dev/null
cp -f $temp/$line $dest/$line 1>/dev/null 2>/dev/null
VerboseOutput 2 "Copied: $line"
done < "/tmp/music-sync-filelist" done < "/tmp/music-sync-filelist"
VerboseOutput "Info" "Copied $line"
} }
CleanUp() { CleanUp() {
VerboseOutput "Info" "Cleaning Up" VerboseOutput 1 "Cleaning Up"
rm "/tmp/music-sync-filelist" if [[ -f /tmp/music-sync-filelist ]]; then
rm -rf "/tmp/converted" rm "/tmp/music-sync-filelist"
VerboseOutput "Info" "Done" 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 0
GetOptions $@ GetOptions $@
CheckDeps 1
if [[ $help == true ]]; then if [[ $help == true ]]; then
Usage Usage
exit exit
fi fi
if [[ -f /tmp/music-sync-filelist ]]; then
rm /tmp/music-sync-filelist
fi
CreateFileList $source $dest "" CreateFileList $source $dest ""
if [[ ! -f /tmp/music-sync-filelist ]]; then if [[ ! -f /tmp/music-sync-filelist ]]; then
VerboseOutput "Info" "Nothing to do!" VerboseOutput 2 "Nothing to do!"
CleanUp
exit 0 exit 0
fi fi
ConvertFiles if [[ $convert == true ]]; then
ConvertFiles
else
if [[ $temp != "/tmp/converted" ]]; then
VerboseOutput 2 "Conversion not enabled. Ignoring cache folder"
fi
temp=$source
fi
CopyFiles CopyFiles
CleanUp CleanUp
ExecTime