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
(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
@ -12,14 +12,34 @@ music-sync <options> <source> <destination>
Syncronises music from one folder to another.
Options:
-s, --source <source> The source folder of the music
-d, --dest <destination> The destionation folder of the music
-v, --verbose Enable verbose output
-h, --help Display this help text
-s, --source <source> The source folder of the music
-d, --dest <destination> The destination folder of the music
-t, --temp <folder> The temporary cache for converted files (default: /tmp/converted)
-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
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:

View File

@ -1,29 +1,67 @@
#!/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=true
convert=false
verbose=2
bitrate=192
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() {
# https://stackoverflow.com/a/29754866
! getopt --test > /dev/null
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
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
OPTIONS=s:d:t:c::a::v::h
LONGOPTS=source:,dest:,temp:,convert::,resize-art::,verbose::,help
# -use ! and PIPESTATUS to get exit code with errexit set
# -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:
eval set -- "$PARSED"
verbose=false
# now enjoy the options in order and nicely split until we see --
while true; do
case "$1" in
-v|--verbose)
verbose=true
verbose=2
if [[ $2 != "" ]]; then
verbose=${2}
shift
fi
shift
;;
-h|--help)
@ -60,18 +101,48 @@ GetOptions() {
dest="$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
break
;;
*)
echo "Programming error"
VerboseOutput 5 "Programming error"
return 3
;;
esac
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
fi
}
@ -85,81 +156,212 @@ Usage() {
echo "Syncronises music from one folder to another."
echo ""
echo "Options:"
echo " -s, --source <source> The source folder of the music"
echo " -d, --dest <destination> The destionation folder of the music"
echo " -v, --verbose Enable verbose output"
echo " -h, --help Display this help text"
echo " -s, --source <source> The source folder of the music"
echo " -d, --dest <destination> The destination folder of the music"
echo " -t, --temp <folder> The temporary cache for converted files (default: /tmp/converted)"
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 ""
}
VerboseOutput() {
if [[ "$verbose" = true ]]; then
echo "[$1] $2" >&2
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/
# ${3} Example-Artist/Example-Album
IFS=""
sourcepath="${1}/*"
sourcepath="${1/\[/\\\[}/*"
sourcepath="${sourcepath/\]/\\\]}"
for file in $sourcepath; do
relfile="${file#"$1/"}"
if [[ -d "${1}/$relfile" ]]; then
origfile="${file#"$1/"}"
relfile=$(echo ${origfile} | sed -e 's/\(\.\)*$//g')
if [[ -d "${1}/$origfile" ]]; then
newdir="${3}/$relfile"
newdir=${newdir#"/"}
VerboseOutput "Info" "Entering $newdir"
CreateFileList "${1}/$relfile" "${2}/$relfile" "$newdir"
elif [[ ! -f "${2}/$relfile" || "${1}/$relfile" -nt "${2}/$relfile" ]]; then
VerboseOutput 1 "Entering $newdir"
CreateFileList "${1}/$origfile" "${2}/$relfile" "$newdir"
elif [[ "${1}/$origfile" != *".m3u" ]] && [[ ! -f "${2}/$relfile" || "${1}/$origfile" -nt "${2}/$relfile" ]]; then
echo ${3}/$relfile >> /tmp/music-sync-filelist
VerboseOutput "Info" "Added: ${3}/${relfile}"
VerboseOutput 2 "Added: ${3}/${relfile}"
fi
done
}
ConvertFiles() {
mkdir -p /tmp/converted
curline=0
percentage=0
while read -r line
do
VerboseOutput "Info" "Converting $line"
if [[ "/tmp/converted/$line" = */* ]]; then
mkdir -p "/tmp/converted/${line%/*}";
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;
lame -b 192 $source/$line /tmp/converted/$line 1>/dev/null 2>/dev/null
VerboseOutput "Info" "Converted $line"
if [[ ! -f "$temp/$line" || "${source}/$line" -nt "$temp/$line" ]]; then
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"
}
CopyFiles() {
curline=0
percentage=0
while read -r line
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
mkdir -p "$dest/${line%/*}";
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"
VerboseOutput "Info" "Copied $line"
}
CleanUp() {
VerboseOutput "Info" "Cleaning Up"
rm "/tmp/music-sync-filelist"
rm -rf "/tmp/converted"
VerboseOutput "Info" "Done"
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 0
GetOptions $@
CheckDeps 1
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 "Info" "Nothing to do!"
VerboseOutput 2 "Nothing to do!"
CleanUp
exit 0
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
CleanUp
CleanUp
ExecTime