bash-music-sync/music-sync.sh

303 lines
6.6 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 getopts 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 <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 destionation folder of the music"
echo " -t, --temp <folder> 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