201 lines
4.6 KiB
Bash
Executable File
201 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat >&2 <<'EOF'
|
|
Usage:
|
|
split-cue-flac.sh /path/to/album-folder [--keep-originals]
|
|
|
|
Behavior:
|
|
- Finds one .cue file in the folder.
|
|
- Finds the matching single .flac file.
|
|
- Splits into a temporary directory inside the album folder.
|
|
- Tags the generated tracks from the CUE.
|
|
- Verifies generated FLACs.
|
|
- Moves split tracks back into the album folder.
|
|
- Deletes the original single FLAC and CUE unless --keep-originals is passed.
|
|
EOF
|
|
exit 2
|
|
}
|
|
|
|
die() {
|
|
echo "ERROR: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
need_cmd() {
|
|
command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
|
|
}
|
|
|
|
[[ $# -ge 1 && $# -le 2 ]] || usage
|
|
|
|
album_dir="${1%/}"
|
|
keep_originals=0
|
|
|
|
if [[ "${2:-}" == "--keep-originals" ]]; then
|
|
keep_originals=1
|
|
elif [[ $# -eq 2 ]]; then
|
|
usage
|
|
fi
|
|
|
|
[[ -d "$album_dir" ]] || die "Not a directory: $album_dir"
|
|
|
|
need_cmd find
|
|
need_cmd shnsplit
|
|
need_cmd flac
|
|
|
|
if command -v cuetag.sh >/dev/null 2>&1; then
|
|
CUETAG_CMD="cuetag.sh"
|
|
elif command -v cuetag >/dev/null 2>&1; then
|
|
CUETAG_CMD="cuetag"
|
|
else
|
|
die "Missing required command: cuetag.sh or cuetag"
|
|
fi
|
|
|
|
# Optional but useful validation.
|
|
HAS_CUEPRINT=0
|
|
if command -v cueprint >/dev/null 2>&1; then
|
|
HAS_CUEPRINT=1
|
|
fi
|
|
|
|
mapfile -d '' cue_files < <(
|
|
find "$album_dir" -maxdepth 1 -type f -iname '*.cue' -print0 | sort -z
|
|
)
|
|
|
|
[[ ${#cue_files[@]} -eq 1 ]] || die "Expected exactly one .cue file, found ${#cue_files[@]}"
|
|
|
|
cue_file="${cue_files[0]}"
|
|
|
|
mapfile -d '' flac_files < <(
|
|
find "$album_dir" -maxdepth 1 -type f -iname '*.flac' -print0 | sort -z
|
|
)
|
|
|
|
[[ ${#flac_files[@]} -ge 1 ]] || die "No .flac files found in $album_dir"
|
|
|
|
# Try to identify the source FLAC.
|
|
# First, if there is exactly one FLAC, use it.
|
|
# Otherwise, try to match the CUE FILE line by basename/stem.
|
|
flac_file=""
|
|
|
|
if [[ ${#flac_files[@]} -eq 1 ]]; then
|
|
flac_file="${flac_files[0]}"
|
|
else
|
|
cue_ref="$(
|
|
awk '
|
|
BEGIN { IGNORECASE=1 }
|
|
/^[[:space:]]*FILE[[:space:]]+/ {
|
|
line=$0
|
|
sub(/^[[:space:]]*FILE[[:space:]]+/, "", line)
|
|
if (line ~ /^"/) {
|
|
sub(/^"/, "", line)
|
|
sub(/"[[:space:]]+[A-Za-z0-9_]+[[:space:]]*$/, "", line)
|
|
print line
|
|
exit
|
|
} else {
|
|
print $2
|
|
exit
|
|
}
|
|
}
|
|
' "$cue_file"
|
|
)"
|
|
|
|
if [[ -n "$cue_ref" ]]; then
|
|
cue_ref_base="$(basename "$cue_ref")"
|
|
cue_ref_stem="${cue_ref_base%.*}"
|
|
|
|
matches=()
|
|
|
|
for f in "${flac_files[@]}"; do
|
|
f_base="$(basename "$f")"
|
|
f_stem="${f_base%.*}"
|
|
|
|
if [[ "$f_base" == "$cue_ref_base" || "$f_stem" == "$cue_ref_stem" ]]; then
|
|
matches+=("$f")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#matches[@]} -eq 1 ]]; then
|
|
flac_file="${matches[0]}"
|
|
fi
|
|
fi
|
|
|
|
[[ -n "$flac_file" ]] || die "Multiple .flac files found and could not determine which one matches the CUE"
|
|
fi
|
|
|
|
echo "Album dir: $album_dir"
|
|
echo "CUE: $(basename "$cue_file")"
|
|
echo "Source: $(basename "$flac_file")"
|
|
|
|
# Validate source FLAC before doing anything.
|
|
echo "Verifying source FLAC..."
|
|
flac -t --silent "$flac_file"
|
|
|
|
if [[ "$HAS_CUEPRINT" -eq 1 ]]; then
|
|
echo "Validating CUE syntax..."
|
|
cueprint "$cue_file" >/dev/null
|
|
fi
|
|
|
|
tmp_dir="$(mktemp -d "$album_dir/.cue-split.tmp.XXXXXXXX")"
|
|
cleanup() {
|
|
if [[ -d "$tmp_dir" ]]; then
|
|
rm -rf -- "$tmp_dir"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
split_dir="$tmp_dir/split"
|
|
original_dir="$tmp_dir/originals"
|
|
|
|
mkdir -p "$split_dir" "$original_dir"
|
|
|
|
echo "Splitting into temporary directory..."
|
|
shnsplit \
|
|
-f "$cue_file" \
|
|
-o flac \
|
|
-t "%n - %t" \
|
|
-d "$split_dir" \
|
|
"$flac_file"
|
|
|
|
mapfile -d '' split_files < <(
|
|
find "$split_dir" -maxdepth 1 -type f -iname '*.flac' -print0 | sort -z
|
|
)
|
|
|
|
[[ ${#split_files[@]} -gt 0 ]] || die "No split FLAC files were produced"
|
|
|
|
echo "Tagging split tracks from CUE..."
|
|
"$CUETAG_CMD" "$cue_file" "${split_files[@]}"
|
|
|
|
echo "Verifying split FLAC files..."
|
|
flac -t --silent "${split_files[@]}"
|
|
|
|
echo "Checking for filename collisions in album directory..."
|
|
for f in "${split_files[@]}"; do
|
|
dest="$album_dir/$(basename "$f")"
|
|
|
|
if [[ -e "$dest" ]]; then
|
|
die "Refusing to overwrite existing file: $dest"
|
|
fi
|
|
done
|
|
|
|
echo "Moving original files aside..."
|
|
mv -- "$flac_file" "$original_dir/"
|
|
mv -- "$cue_file" "$original_dir/"
|
|
|
|
echo "Moving split tracks into album directory..."
|
|
for f in "${split_files[@]}"; do
|
|
mv -- "$f" "$album_dir/"
|
|
done
|
|
|
|
if [[ "$keep_originals" -eq 1 ]]; then
|
|
keep_dir="$album_dir/.cue-split-originals"
|
|
mkdir -p "$keep_dir"
|
|
|
|
echo "Keeping originals in: $keep_dir"
|
|
mv -- "$original_dir/"* "$keep_dir/"
|
|
else
|
|
echo "Deleting original single FLAC and CUE..."
|
|
rm -rf -- "$original_dir"
|
|
fi
|
|
|
|
echo "Done."
|