djcev.com

//

Git Repos / dotfiles / bin / scs4tool.sh

Last commit to this repo was on 2023-04-15 at 11:53.

#!/bin/sh
#
# Copyright (c) 2015-2022, Cameron Vanderzanden. Available under the
# terms of the 2-clause BSD license. Please see the file "LICENSE" in
# the root dir of this repository for more information.
#
#- scs4tool.sh 2015/03/28 cev
#- scs4tool.sh 2022/01/30 cev
## Usage: scs4tool.sh [-3hHlv] [-a dir] [-d dir] [-r dir]
##
## Manage a file tree for use on the Stanton SCS.4DJ music player. Performs
## a function similar to Stanton's Quickgrid software.
##
## This script requires a working installation of wine and "djanalyze.exe",
## which can be found in the latest version of Stanton Quickgrid for
## Windows. This script also requires ffmpeg, flac/libFlac, and sox.
##
## Top level options:
## -3 Convert flac files to mp3 before copying.
## -a Add a directory to the destination.
## -d Destination directory.
## -h Show help.
## -H Print the "hashes.dat" hashtable.
## -l View output log when finished.
## -r Remove a directory at the destination.
## -V Print version info.
##
#

if [ -x $HOME/bin/cev_print.sh ]; then
. $HOME/bin/cev_print.sh
else
print_help() { grep "^## " "$0" | cut -c 4-; }
print_version() { grep "^#- " "$0" | cut -c 4-; }
fi

self="${0##*/}"
prefix="${SCS4_PREFIX:-/y}"
wine="/usr/local/wine-proton/bin/wine"
database="${SCS4_DATABASE_PREFIX:-${prefix}/SCS.4DJ_Database}"
hashtable="${SCS4_HASHTABLE_PATH:-${database}/hashes.dat}"
tmpdir=""
logfile=""
viewlog=""
flactomp3="N"
flacapplyrg="Y"

[ -z "$WINEPREFIX" ] && WINEPREFIX="/z/warez/SCS4_DjAnalyze"
[ -z "$WINEDEBUG" ] && WINEDEBUG="-all"
export WINEPREFIX WINEDEBUG
export WINEARCH=win32
djanalyze="/z/warez/SCS4_DjAnalyze/djanalyze.exe"

cleanup() {
[ -n "$logfile" -a -f "$logfile" ] && rm $logfile
[ -n "$tmpdir" -a -d "$tmpdir" ] && rmdir $tmpdir
}

close() {
code=$1 && shift
[ $code -eq 1 ] && printf "%b%s:%b \"%s\" %bnot found.%b\n" \
"$_self" "$self" "$_error" "$@" "$_norm" "$_clear"
[ -n "$viewlog" ] && $PAGER $logfile
exit $code
}

# https://stackoverflow.com/questions/642464/convert-unix-path-to-dos-path
unix_to_dos_path() { printf 'z:%s\n' "$@" | sed 's/\//\\/g'; }

change_prefix() {
prefix="$@"
database="${prefix}/SCS.4DJ_Database"
hashtable="${database}/hashes.dat"
}

add_analysis() {
dosfile="$(unix_to_dos_path "$@")"
dostmpdir="$(unix_to_dos_path "$tmpdir")"
printf "wine %s %s %s %s\n" \
"$djanalyze" "$dosfile" "$dostmpdir" "$hash" 1>>$logfile 2>&1
$wine "$djanalyze" "$dosfile" "$dostmpdir" "$hash" 1>>$logfile 2>&1
# djanalyze.exe creates a folder named 'SCS.8DJ_Database' by default.
# wildcard our way into it.
cp -vr $tmpdir/*/analysis/* "${database}/analysis/" 2>&1 1>>$logfile
rm -rfv $tmpdir/SCS.?DJ* 2>&1 1>>$logfile
}

add_hashtable_entry() {
# five fields, seperated by tab:
# filename (unescaped)\t MD5 hash\t st_atime+'000'\t st_size\t 0
name="${@##*${prefix}}"
printf "%s\t%s\t%s\t%s\t%s\n" \
"$name" "$hash" "${st_atime}000" "$st_size" "0" >>$tmpdir/hashes.dat
cat $tmpdir/hashes.dat >> "$hashtable"
rm $tmpdir/hashes.dat
}

add_file_end() {
# GNU md5sum, FreeBSD/OSX md5 -q, NetBSD md5 (two field output)
hash="$(md5 -q "$@")"
# FreeBSD/NetBSD/OSX stat -s, GNU stat (different format)
eval $(stat -s "$@")
add_hashtable_entry "$@"
add_analysis "$@"
}

add_file_convertflactomp3() {
bl="$(basename "$@")"
printf "%b%s:%b converting %b%.52s...%b\n" \
"$_self" "$self" "$_norm" "$_high" "$bl" "$_clear"
tl="$(basename "$@" flac)"
fl="$(printf "%s\n" "$@" | sed "s/\.flac/\.mp3/")"
flac_to_mp3.sh -r -o "$tmpdir/${tl}mp3" "$l"
# TODO error checking
printf "%b%s:%b adding %b%.56s...%b\n" \
"$_self" "$self" "$_norm" "$_high" "${tl}mp3" "$_clear"
cp -pv "$tmpdir/${tl}mp3" "$prefix/$fl" 2>/dev/null 1>>$logfile
add_file_end "$prefix/$fl"
[ -f "$tmpdir/${tl}mp3" ] && \
rm -v "$tmpdir/${tl}mp3" 2>/dev/null 1>>$logfile
}

add_file_flacresample() {
pat="bits_per_sample\|sample_rate"
# TODO error checking; maybe check filename and make sure it's a .wav ?
# TODO probably shouldn't be using eval here
eval $(ffprobe -v quiet -show_streams "$@" | /usr/bin/grep "$pat")
if [ "$sample_rate" -ne "44100" -o "$bits_per_sample" -ne "16" ]; then
if [ "$sample_rate" -eq "44100" ]; then
printf "%b%s:%b changing bit depth...%b\n" \
"$_self" "$self" "$_norm" "$_clear"
sox "$@" -b 16 "$@.rs.wav" 2>/dev/null 1>>$logfile
else
printf "%b%s:%b resampling to 44.1khz 16bps...%b\n" \
"$_self" "$self" "$_norm" "$_clear"
sox "$@" -b 16 "$@.rs.wav" rate -v 44.1k 2>/dev/null 1>>$logfile
fi
mv -v "$@.rs.wav" "$@" 2>/dev/null 1>>$logfile
fi
unset sample_rate && unset bits_per_sample
}

add_file_applyflacreplaygain() {
bl="$(basename "$@")"
# reencode flac files and apply replaygain; written 2020/11/03
tl="$(basename "$@" flac)"
printf "%b%s:%b adding %b%.56s...%b\n" \
"$_self" "$self" "$_norm" "$_high" "$bl" "$_clear"
flac --apply-replaygain-which-is-not-lossless=-2tLn1 \
--totally-silent -d -o "$tmpdir/${tl}wav" "$@"
add_file_flacresample "$tmpdir/${tl}wav"
flac --totally-silent -o "$prefix/$@" "$tmpdir/${tl}wav"
metaflac --no-utf8-convert \
--export-tags-to="$tmpdir/${tl}tags" "$@"
sed -i '' '/REPLAY/d' "$tmpdir/${tl}tags"
metaflac --remove-all-tags \
--import-tags-from="$tmpdir/${tl}tags" "$prefix/$@"
# TODO: transfer cover
[ -f "$tmpdir/${tl}wav" ] && rm -v "$tmpdir/${tl}wav" \
2>/dev/null 1>>$logfile
[ -f "$tmpdir/${tl}tags" ] && rm -v "$tmpdir/${tl}tags" \
2>/dev/null 1>>$logfile
add_file_end "$prefix/$@"
}

add_file() {
[ ! -e "$@" ] && close 1 "$@"
find "$@" -print | while read l; do
if [ -d "$l" ]; then
printf "%b%s:%b creating %b%.54s...%b\n" \
"$_self" "$self" "$_norm" "$_high" "$l" "$_clear"
mkdir -p "$prefix/$l" 2>&1 1>>$logfile
else
bl="$(basename "$l")"
if [ "${bl##*.}" = "flac" -a "$flactomp3" = "Y" ]; then
add_file_convertflactomp3 "$l"
elif [ "${bl##*.}" = "flac" -a "$flacapplyrg" = "Y" ]; then
add_file_applyflacreplaygain "$l"
elif [ "${bl##*.}" = "flac" -o "${bl##*.}" = "mp3" ]; then
printf "%b%s:%b adding %b%.56s...%b\n" \
"$_self" "$self" "$_norm" "$_high" "$bl" "$_clear"
cp -pv "$l" "$prefix/$l" 2>/dev/null 1>>$logfile
add_file_end "$prefix/$l"
fi
fi
done
}

cmd_add() {
[ ! -d "$database" ] && mkdir -p "$database"
[ ! -d "$database/analysis" ] && mkdir -p "$database/analysis"
[ ! -e "$hashtable" ] && printf "Hash Helper v1\n" > "$hashtable" # heh
# a trailing "/" will mess up hashtable.dat. The SCS.4DJ will not match
# "/dir//file" and "/dir/file".
if [ "$(printf "%s\n" "$@" | rev | cut -c 1)" = "/" ]; then
f="${@%?}"
else
f="$@"
fi
tmpdir=$(mktemp -d ${TMPDIR:-/tmp}/${self}.XXXXXXXX)
logfile="$tmpdir/log" && touch $logfile
trap cleanup EXIT
add_file "$f"
close 0
}

# delete analysis files based on md5sum
del_analysis() {
# first two characters of hash remaining characters of hash
h1="$(printf "%s\n" "$@" | cut -c 1-2)"; h2="${@#??*}"
if [ -d $database/analysis/$h1 ]; then
rm -v $database/analysis/$h1/$h2.sar 2>&1 1>>$logfile
rm -v $database/analysis/$h1/$h2.sek 2>&1 1>>$logfile
rm -v $database/analysis/$h1/$h2.wfv 2>&1 1>>$logfile
fi
}

# delete hashtable entry based on md5sum
del_hashtable_entry() {
sed "/$@/d" $hashtable > $tmpdir/hashes.dat \
&& cp -v $tmpdir/hashes.dat $hashtable 2>&1 1>>$logfile \
&& rm $tmpdir/hashes.dat
}

del_file() {
[ ! -e "$@" ] && close 1 "$@"
find "$@" -d -print | while read l; do
if [ -d "$l" ]; then
printf "%b%s:%b removing %b%.54s...%b\n" \
"$_self" "$self" "$_norm" "$_high" "${l##${prefix}/}" "$_clear"
rm -dv "$l" 2>&1 1>>$logfile
else
printf "%b%s:%b removing %b%.54s...%b\n" \
"$_self" "$self" "$_norm" "$_high" "${l##${prefix}/}" "$_clear"
hash="$(md5 -q "$l")"
rm -v "$l" 2>&1 1>>$logfile
del_analysis "$hash"
[ -f "$hashtable" ] && del_hashtable_entry "$hash"
fi
done
}

# delete file, delete related analysis files, delete any hashes.dat entries.
cmd_del() {
f="$prefix/$@"
tmpdir=$(mktemp -d ${TMPDIR:-/tmp}/${self}.XXXXXXXX)
logfile="$tmpdir/log" && touch $logfile
trap cleanup EXIT
del_file "$f"
close 0
}

# TODO this is left hanging; hook this back into the option block or delete it
cmd_clear() {
[ "$1" = "hash" ] && rm -v "$hashtable"
}

cmd_hashtable() {
if [ -f "$hashtable" ]; then
cat $hashtable;
else
printf "%b%s:%b no hashtable in %s prefix.%b\n" \
"$_self" "$self" "$_norm" "${prefix}" "$_clear"
exit 1
fi
}

cmd_list() { ls "$prefix/$@"; }

# top level options
while getopts "3a:d:hHlr:V?" option; do
case $option in
3) flactomp3="Y" ;;
a) cmd_add "$OPTARG" && exit 0;;
d) change_prefix "$OPTARG" ;;
h) print_help && exit 0 ;;
H) cmd_hashtable && exit 0 ;;
l) viewlog="Y" ;;
r) cmd_del "$OPTARG" && exit 0 ;;
V) print_version && exit 0 ;;
"?") print_help && exit 0 ;;
esac
shift $((OPTIND - 1))
done

cmd_list "$@" && exit 0

Return to top of this page or return to the overview of this repository