Merge branch 'develop'
This commit is contained in:
commit
962748da1e
32
README.MD
32
README.MD
@ -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:
|
||||||
|
|
||||||
|
300
music-sync.sh
300
music-sync.sh
@ -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 getopt’s output this way to handle the quoting right:
|
# read getopt’s 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
|
||||||
|
Loading…
Reference in New Issue
Block a user