Compare commits
4 Commits
623e83a46d
...
c6be37116d
Author | SHA1 | Date |
---|---|---|
Jeroen De Meerleer | c6be37116d | |
Jeroen De Meerleer | 9f617b3c55 | |
Jeroen De Meerleer | b2f66fc057 | |
Jeroen De Meerleer | 14f1dd54bd |
307
music-sync.sh
307
music-sync.sh
|
@ -1,4 +1,38 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
#/
|
||||||
|
#/ Usage:
|
||||||
|
#/ music-sync <options> -s|--source <source> -d|--dest <destination>
|
||||||
|
#/ 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 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)
|
||||||
|
#/ -j, --jobs <nproc> Number of processes to use in multi-threading (default: CPU_CORES - 2)
|
||||||
|
#/ -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
|
||||||
|
#/
|
||||||
|
|
||||||
begin=$(date +"%s")
|
begin=$(date +"%s")
|
||||||
# saner programming env: these switches turn some bugs into errors
|
# saner programming env: these switches turn some bugs into errors
|
||||||
|
@ -12,6 +46,10 @@ help=false
|
||||||
temp="/tmp/converted"
|
temp="/tmp/converted"
|
||||||
convertart=false
|
convertart=false
|
||||||
coverartsize=200
|
coverartsize=200
|
||||||
|
jobcount="-2"
|
||||||
|
multithread=false
|
||||||
|
script_name=$(basename "${0}")
|
||||||
|
script_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||||
|
|
||||||
CheckDeps() {
|
CheckDeps() {
|
||||||
if [[ $1 == 0 ]]; then
|
if [[ $1 == 0 ]]; then
|
||||||
|
@ -60,6 +98,17 @@ CheckDeps() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
VerboseOutput 0 "\`convert --version\` succeeded"
|
VerboseOutput 0 "\`convert --version\` succeeded"
|
||||||
|
|
||||||
|
# Check Gnu parallel
|
||||||
|
if [[ $multithread == true && ! $(parallel -h 2>/dev/null) ]]; then
|
||||||
|
VerboseOutput 5 "\`parallel -h\` failed"
|
||||||
|
VerboseOutput 5 "Sorry, It seems that parallel is not installed on your system"
|
||||||
|
VerboseOutput 5 "Please install gnu-parallel and env_parallel from your repositories and make sure it is available in your \$PATH"
|
||||||
|
VerboseOutput 5 "Otherwise disable multithreading"
|
||||||
|
ExecTime
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VerboseOutput 0 "\`parallel -h \` succeeded"
|
||||||
fi
|
fi
|
||||||
VerboseOutput 1 "Dependency test OK"
|
VerboseOutput 1 "Dependency test OK"
|
||||||
}
|
}
|
||||||
|
@ -67,8 +116,8 @@ CheckDeps() {
|
||||||
GetOptions() {
|
GetOptions() {
|
||||||
|
|
||||||
# https://stackoverflow.com/a/29754866
|
# https://stackoverflow.com/a/29754866
|
||||||
OPTIONS=s:d:t:c::a::v::h
|
OPTIONS=s:d:t:c::a::v::hj::
|
||||||
LONGOPTS=source:,dest:,temp:,convert::,resize-art::,verbose::,help
|
LONGOPTS=source:,dest:,temp:,convert::,resize-art::,verbose::,help,jobs::
|
||||||
|
|
||||||
# -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
|
||||||
|
@ -140,6 +189,17 @@ GetOptions() {
|
||||||
shift
|
shift
|
||||||
VerboseOutput 1 "Album art will ${coverartsize}px wide"
|
VerboseOutput 1 "Album art will ${coverartsize}px wide"
|
||||||
;;
|
;;
|
||||||
|
-j|--jobs)
|
||||||
|
VerboseOutput 0 "--jobs given"
|
||||||
|
multithread=true
|
||||||
|
jobcount="-2"
|
||||||
|
if [[ $2 != "" ]]; then
|
||||||
|
jobcount=${2}
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
VerboseOutput 1 "multithreading will use ${jobcount} threads"
|
||||||
|
;;
|
||||||
--)
|
--)
|
||||||
shift
|
shift
|
||||||
break
|
break
|
||||||
|
@ -170,43 +230,11 @@ GetOptions() {
|
||||||
if [[ $dest == "-" ]] || [[ $source == "-" ]]; then
|
if [[ $dest == "-" ]] || [[ $source == "-" ]]; then
|
||||||
help=true
|
help=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VerboseOutput 1 "Checks OK. Going on"
|
VerboseOutput 1 "Checks OK. Going on"
|
||||||
}
|
}
|
||||||
|
|
||||||
Usage() {
|
Usage() {
|
||||||
echo ""
|
grep '^#/' "${script_dir}/${script_name}" | sed 's/^#\/\w*//'
|
||||||
echo "Usage:"
|
|
||||||
echo "music-sync <options> -s|--source <source> -d|--dest <destination>"
|
|
||||||
echo "music-sync <options> <source> <destination>"
|
|
||||||
echo ""
|
|
||||||
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 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() {
|
VerboseOutput() {
|
||||||
|
@ -267,109 +295,126 @@ CreateFileList() {
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
ConvertFiles() {
|
Execute() {
|
||||||
curline=0
|
curline=0
|
||||||
percentage=0
|
percentage=0
|
||||||
IFS="
|
IFS="
|
||||||
";
|
";
|
||||||
for line in $(cat "/tmp/music-sync-filelist")
|
if [[ $multithread == true ]]; then
|
||||||
do
|
VerboseOutput 3 "Running in parallel. Mind your load"
|
||||||
VerboseOutput 0 "${source}/$line"
|
export -f ProcessFile
|
||||||
if [[ ! -f "${source}/$line" ]]; then
|
export -f ConvertFile
|
||||||
VerboseOutput 5 "Source-file ${source}/$line Unreachable"
|
export -f CopyFile
|
||||||
ExecTime
|
export -f VerboseOutput
|
||||||
exit 3
|
export -f ExecTime
|
||||||
|
export dest
|
||||||
|
export source
|
||||||
|
export bitrate
|
||||||
|
export convert
|
||||||
|
export temp
|
||||||
|
export verbose
|
||||||
|
export coverartsize
|
||||||
|
export begin
|
||||||
|
parallel --jobs ${jobcount} --line-buffer --arg-file "/tmp/music-sync-filelist" ProcessFile
|
||||||
|
else
|
||||||
|
for line in $(cat "/tmp/music-sync-filelist")
|
||||||
|
do
|
||||||
|
|
||||||
|
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 2 "Current file: $line"
|
||||||
|
VerboseOutput 2 "Progress: $curline / $total (${percentage%00}%)"
|
||||||
|
ProcessFile $line
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
VerboseOutput 2 "Done!"
|
||||||
|
}
|
||||||
|
ProcessFile() {
|
||||||
|
line=${1}
|
||||||
|
VerboseOutput 2 "Current File: $line"
|
||||||
|
if [[ $convert == true ]]; then
|
||||||
|
ConvertFile "$line"
|
||||||
|
fi
|
||||||
|
CopyFile "$line"
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvertFile() {
|
||||||
|
line=${1}
|
||||||
|
if [[ ! -f "${source}/$line" ]]; then
|
||||||
|
VerboseOutput 5 "Source-file ${source}/$line Unreachable"
|
||||||
|
ExecTime
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$temp/$line" = */* ]]; then
|
||||||
|
VerboseOutput 0 "Creating folder $temp/${line%/*}"
|
||||||
|
mkdir -p "$temp/${line%/*}";
|
||||||
|
fi;
|
||||||
|
mp3line=$(echo "$line" | sed -e 's/.flac/.mp3/')
|
||||||
|
if [[ ! -f "$temp/$mp3line" || "${source}/$mp3line" -nt "$temp/$mp3line" ]]; then
|
||||||
|
if [[ $line != *".mp3" ]]; then
|
||||||
|
VerboseOutput 3 "${line} will be converted to mp3-file ${mp3line}"
|
||||||
fi
|
fi
|
||||||
|
VerboseOutput 0 "Converting MP3-file $line"
|
||||||
|
ffmpeg -y -i "$source/$line" -acodec libmp3lame -map_metadata 0 -id3v2_version 3 -b:a ${bitrate}k "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
||||||
|
if [[ $convertart == true ]]; then
|
||||||
|
VerboseOutput 0 "Creating folder $temp/${mp3line}-images/"
|
||||||
|
mkdir -p "$temp/${mp3line}-images/"
|
||||||
|
VerboseOutput 0 "Extracted albumart"
|
||||||
|
eyeD3 --write-images "$temp/${mp3line}-images/" "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
||||||
|
frontcovers=(${temp}/${mp3line}-images/FRONT_COVER.*)
|
||||||
|
if [ -e "$frontcovers" ]; then
|
||||||
|
VerboseOutput 0 "Converting albumart"
|
||||||
|
convert "$temp/${mp3line}-images/FRONT_COVER.*" -resize ${coverartsize}x${coverartsize} "$temp/${mp3line}-images/FRONT_COVER.jpg" 1>/dev/null 2>/dev/null
|
||||||
|
eyeD3 --remove-all-images "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
||||||
|
VerboseOutput 0 "Embedding albumart"
|
||||||
|
eyeD3 --add-image "$temp/${mp3line}-images/FRONT_COVER.jpg:FRONT_COVER" "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
||||||
|
VerboseOutput 1 "Converted cover art: ${mp3line}"
|
||||||
|
else
|
||||||
|
VerboseOutput 4 "No front cover art found for ${mp3line}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
VerboseOutput 1 "Converted: $line"
|
||||||
|
else
|
||||||
|
VerboseOutput 3 "$line already converted"
|
||||||
|
fi;
|
||||||
|
}
|
||||||
|
|
||||||
curline=$(expr ${curline} + 1)
|
CopyFile() {
|
||||||
total=$(cat /tmp/music-sync-filelist | wc -l)
|
line=${1}
|
||||||
percentage=$(echo "scale=4;${curline}/${total}" | bc)
|
if [[ ! -d "$dest" ]]; then
|
||||||
percentage=$(echo "scale=2;${percentage}*100" | bc)
|
VerboseOutput 5 "Destination unreachable"
|
||||||
VerboseOutput 1 "Converting: $line"
|
ExecTime
|
||||||
VerboseOutput 2 "Progress: $curline / $total (${percentage%00}%) Step 1 of 2"
|
exit 4
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$temp/$line" = */* ]]; then
|
if [[ $convert == true ]]; then
|
||||||
VerboseOutput 0 "Creating folder $temp/${line%/*}"
|
|
||||||
mkdir -p "$temp/${line%/*}";
|
|
||||||
fi;
|
|
||||||
mp3line=$(echo "$line" | sed -e 's/.flac/.mp3/')
|
mp3line=$(echo "$line" | sed -e 's/.flac/.mp3/')
|
||||||
if [[ ! -f "$temp/$mp3line" || "${source}/$mp3line" -nt "$temp/$mp3line" ]]; then
|
else
|
||||||
if [[ $line != *".mp3" ]]; then
|
mp3line=$(echo "$line")
|
||||||
VerboseOutput 3 "${line} will be converted to mp3-file ${mp3line}"
|
fi
|
||||||
fi
|
|
||||||
VerboseOutput 0 "Converting MP3-file $line"
|
|
||||||
ffmpeg -y -i "$source/$line" -acodec libmp3lame -map_metadata 0 -id3v2_version 3 -b:a ${bitrate}k "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
|
||||||
if [[ $convertart == true ]]; then
|
|
||||||
VerboseOutput 0 "Creating folder $temp/${mp3line}-images/"
|
|
||||||
mkdir -p "$temp/${mp3line}-images/"
|
|
||||||
VerboseOutput 0 "Extracted albumart"
|
|
||||||
eyeD3 --write-images "$temp/${mp3line}-images/" "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
|
||||||
frontcovers=(${temp}/${mp3line}-images/FRONT_COVER.*)
|
|
||||||
if [ -e "$frontcovers" ]; then
|
|
||||||
VerboseOutput 0 "Converting albumart"
|
|
||||||
convert "$temp/${mp3line}-images/FRONT_COVER.*" -resize ${coverartsize}x${coverartsize} "$temp/${mp3line}-images/FRONT_COVER.jpg" 1>/dev/null 2>/dev/null
|
|
||||||
eyeD3 --remove-all-images "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
|
||||||
VerboseOutput 0 "Embedding albumart"
|
|
||||||
eyeD3 --add-image "$temp/${mp3line}-images/FRONT_COVER.jpg:FRONT_COVER" "$temp/${mp3line}" 1>/dev/null 2>/dev/null
|
|
||||||
VerboseOutput 1 "Converted cover art: ${mp3line}"
|
|
||||||
else
|
|
||||||
VerboseOutput 4 "No front cover art found for ${mp3line}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
VerboseOutput 2 "Converted: $line"
|
|
||||||
else
|
|
||||||
VerboseOutput 3 "$line already converted"
|
|
||||||
fi;
|
|
||||||
|
|
||||||
done
|
if [[ ! -f "${temp}/$mp3line" ]]; then
|
||||||
VerboseOutput 2 "Done converting files"
|
VerboseOutput 5 "Source-file ${temp}/$mp3line Unreachable"
|
||||||
|
ExecTime
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
destline=$(echo $mp3line | sed -e 's/\.*\//\//g')
|
||||||
|
VerboseOutput 1 "Copying: $line"
|
||||||
|
|
||||||
|
if [[ "$dest/$line" = */* ]]; then
|
||||||
|
VerboseOutput 0 "Creating folder $dest/${line%/*}"
|
||||||
|
mkdir -p "$dest/${destline%/*}";
|
||||||
|
fi;
|
||||||
|
|
||||||
|
cp -f "$temp/$mp3line" "$dest/${destline}" 1>/dev/null 2>/dev/null
|
||||||
|
|
||||||
|
VerboseOutput 1 "Copied: $line"
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyFiles() {
|
|
||||||
curline=0
|
|
||||||
percentage=0
|
|
||||||
IFS="
|
|
||||||
";
|
|
||||||
for line in $(cat "/tmp/music-sync-filelist")
|
|
||||||
do
|
|
||||||
if [[ ! -d "$dest" ]]; then
|
|
||||||
VerboseOutput 5 "Destination unreachable"
|
|
||||||
ExecTime
|
|
||||||
exit 4
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $convert == true ]]; then
|
|
||||||
mp3line=$(echo "$line" | sed -e 's/.flac/.mp3/')
|
|
||||||
else
|
|
||||||
mp3line=$(echo "$line")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "${temp}/$mp3line" ]]; then
|
|
||||||
VerboseOutput 5 "Source-file ${temp}/$mp3line 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)
|
|
||||||
destline=$(echo $mp3line | sed -e 's/\.*\//\//g')
|
|
||||||
VerboseOutput 1 "Copying: $line"
|
|
||||||
VerboseOutput 2 "Progress: $curline / $total (${percentage%00}%) Step 2 of 2"
|
|
||||||
|
|
||||||
if [[ "$dest/$line" = */* ]]; then
|
|
||||||
VerboseOutput 0 "Creating folder $dest/${line%/*}"
|
|
||||||
mkdir -p "$dest/${destline%/*}";
|
|
||||||
fi;
|
|
||||||
|
|
||||||
cp -f $temp/$mp3line $dest/${destline} 1>/dev/null 2>/dev/null
|
|
||||||
|
|
||||||
VerboseOutput 2 "Copied: $line"
|
|
||||||
|
|
||||||
done
|
|
||||||
VerboseOutput 2 "Done copying files"
|
|
||||||
}
|
|
||||||
|
|
||||||
CleanUp() {
|
CleanUp() {
|
||||||
VerboseOutput 1 "Cleaning Up"
|
VerboseOutput 1 "Cleaning Up"
|
||||||
|
@ -418,14 +463,12 @@ if [[ ! -f /tmp/music-sync-filelist ]]; then
|
||||||
CleanUp
|
CleanUp
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
if [[ $convert == true ]]; then
|
if [[ $convert == false ]]; then
|
||||||
ConvertFiles
|
|
||||||
else
|
|
||||||
if [[ $temp != "/tmp/converted" ]]; then
|
if [[ $temp != "/tmp/converted" ]]; then
|
||||||
VerboseOutput 2 "Conversion not enabled. Ignoring cache folder"
|
VerboseOutput 2 "Conversion not enabled. Ignoring cache folder"
|
||||||
fi
|
fi
|
||||||
temp=$source
|
temp=$source
|
||||||
fi
|
fi
|
||||||
CopyFiles
|
Execute
|
||||||
CleanUp
|
CleanUp
|
||||||
ExecTime
|
ExecTime
|
||||||
|
|
Loading…
Reference in New Issue