fix: script splits on / (no spaces needed), exceptions for Au/Ra & HUNTR/X
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
3a8b879abf
commit
70fd299637
1 file changed
+47
-15
+47
-15
@@ -1,18 +1,13 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# fix-artist-separators.sh
|
# fix-artist-separators.sh
|
||||||
# Replaces " / " (space-slash-space) in audio file artist tags with ";"
|
# Splits artist tags on "/" into ";" — with hardcoded exceptions for artist
|
||||||
#
|
# names that legitimately contain "/" (Au/Ra, HUNTR/X).
|
||||||
# Safe: only matches " / " with surrounding spaces, so Au/Ra and HUNTR/X
|
|
||||||
# (no spaces around the slash) are left completely untouched.
|
|
||||||
#
|
#
|
||||||
# Requires: ffprobe, ffmpeg, python3
|
# Requires: ffprobe, ffmpeg, python3
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# bash fix-artist-separators.sh [music_dir] -- dry run (no changes)
|
# bash fix-artist-separators.sh [music_dir] -- dry run (no changes)
|
||||||
# bash fix-artist-separators.sh [music_dir] --apply -- actually modify files
|
# bash fix-artist-separators.sh [music_dir] --apply -- actually modify files
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# bash fix-artist-separators.sh /opt/navidrome/music --apply
|
|
||||||
|
|
||||||
MUSIC_DIR="${1:-/opt/navidrome/music}"
|
MUSIC_DIR="${1:-/opt/navidrome/music}"
|
||||||
APPLY=false
|
APPLY=false
|
||||||
@@ -26,15 +21,54 @@ fi
|
|||||||
if $APPLY; then
|
if $APPLY; then
|
||||||
echo "🔧 APPLY mode — files will be modified"
|
echo "🔧 APPLY mode — files will be modified"
|
||||||
else
|
else
|
||||||
echo "🔍 DRY RUN — no files will be changed (add --apply to apply changes)"
|
echo "🔍 DRY RUN — no files will be changed (add --apply to apply)"
|
||||||
fi
|
fi
|
||||||
echo "📂 Scanning: $MUSIC_DIR"
|
echo "📂 Scanning: $MUSIC_DIR"
|
||||||
|
echo "🚫 Exceptions: Au/Ra, HUNTR/X"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
count_found=0
|
count_found=0
|
||||||
count_ok=0
|
count_ok=0
|
||||||
count_fail=0
|
count_fail=0
|
||||||
|
|
||||||
|
# Splits artist string on "/" while preserving exception names.
|
||||||
|
# Example: "HUNTR/X/EJAE/AUDREY NUNA" -> "HUNTR/X;EJAE;AUDREY NUNA"
|
||||||
|
# "-Prey/zecki" -> "-Prey;zecki"
|
||||||
|
# "Au/Ra" -> "Au/Ra" (exception, unchanged)
|
||||||
|
SPLIT_PY='
|
||||||
|
import sys
|
||||||
|
|
||||||
|
EXCEPTIONS = ["Au/Ra", "HUNTR/X"]
|
||||||
|
exc_lookup = [(exc, exc.lower(), exc.split("/")) for exc in EXCEPTIONS]
|
||||||
|
|
||||||
|
def smart_split(artist):
|
||||||
|
parts = artist.split("/")
|
||||||
|
result = []
|
||||||
|
i = 0
|
||||||
|
while i < len(parts):
|
||||||
|
matched = False
|
||||||
|
for exc, exc_lower, exc_parts in exc_lookup:
|
||||||
|
n = len(exc_parts)
|
||||||
|
candidate = parts[i:i+n]
|
||||||
|
if len(candidate) == n and "/".join(candidate).lower() == exc_lower:
|
||||||
|
result.append(exc)
|
||||||
|
i += n
|
||||||
|
matched = True
|
||||||
|
break
|
||||||
|
if not matched:
|
||||||
|
p = parts[i].strip()
|
||||||
|
if p:
|
||||||
|
result.append(p)
|
||||||
|
i += 1
|
||||||
|
return ";".join(result)
|
||||||
|
|
||||||
|
artist = sys.stdin.read().strip()
|
||||||
|
if "/" not in artist:
|
||||||
|
print(artist)
|
||||||
|
else:
|
||||||
|
print(smart_split(artist))
|
||||||
|
'
|
||||||
|
|
||||||
get_artist() {
|
get_artist() {
|
||||||
ffprobe -v quiet -print_format json -show_format "$1" 2>/dev/null \
|
ffprobe -v quiet -print_format json -show_format "$1" 2>/dev/null \
|
||||||
| python3 -c "
|
| python3 -c "
|
||||||
@@ -49,9 +83,10 @@ while IFS= read -r -d '' file; do
|
|||||||
artist=$(get_artist "$file") || continue
|
artist=$(get_artist "$file") || continue
|
||||||
[[ "$artist" != *"/"* ]] && continue
|
[[ "$artist" != *"/"* ]] && continue
|
||||||
|
|
||||||
new_artist="${artist// \/ /;}"
|
new_artist=$(echo "$artist" | python3 -c "$SPLIT_PY" 2>/dev/null) || continue
|
||||||
count_found=$((count_found + 1))
|
[[ "$artist" == "$new_artist" ]] && continue
|
||||||
|
|
||||||
|
count_found=$((count_found + 1))
|
||||||
echo "📄 $file"
|
echo "📄 $file"
|
||||||
echo " Before : $artist"
|
echo " Before : $artist"
|
||||||
echo " After : $new_artist"
|
echo " After : $new_artist"
|
||||||
@@ -77,13 +112,10 @@ done < <(find "$MUSIC_DIR" -type f \( \
|
|||||||
-name "*.opus" \) -print0)
|
-name "*.opus" \) -print0)
|
||||||
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
echo "Files with \" / \" in artist tag: $count_found"
|
echo "Files to change: $count_found"
|
||||||
if $APPLY; then
|
if $APPLY; then
|
||||||
echo "Updated: $count_ok | Failed: $count_fail"
|
echo "Updated: $count_ok | Failed: $count_fail"
|
||||||
if [[ $count_ok -gt 0 ]]; then
|
[[ $count_ok -gt 0 ]] && echo "" && echo "💡 Trigger a Navidrome rescan to pick up the changes."
|
||||||
echo ""
|
|
||||||
echo "💡 Trigger a Navidrome rescan to pick up the changes."
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
echo "Run with --apply to fix them:"
|
echo "Run with --apply to fix them:"
|
||||||
|
|||||||
Reference in new issue
Block a user