Files
susan-scripts/import_music.sh
Caine c7956ae9b2 Initial commit: susan automation scripts
Overnight transcoding, music discovery/import, system health reports,
stats page generator, and bookmark management.

Secrets stored in /etc/automation/ — not in repo.
2026-02-15 09:41:49 +00:00

132 lines
3.9 KiB
Bash
Executable File

#!/bin/bash
# Import, transcode, and organize music from slskd downloads
# Usage: ./import_music.sh [--dry-run] [--keep-flac]
INGEST_DIR="/disks/Plex/Music/_ingest"
LIBRARY_DIR="/disks/Plex/Music"
GROUP="mediaserver"
BITRATE="128k"
DRY_RUN=false
KEEP_FLAC=false
for arg in "$@"; do
case $arg in
--dry-run) DRY_RUN=true ;;
--keep-flac) KEEP_FLAC=true ;;
esac
done
echo "=== Music Import Started ==="
$DRY_RUN && echo "DRY RUN MODE"
# Skip these directories
shopt -s extglob
cd "$INGEST_DIR" || exit 1
for album_dir in */; do
[[ ! -d "$album_dir" ]] && continue
dir_name="${album_dir%/}"
# Skip special dirs
[[ "$dir_name" == "incomplete" ]] && continue
[[ "$dir_name" == "downloads" ]] && continue
# Count audio files
audio_count=$(ls -1 "$album_dir"*.flac "$album_dir"*.mp3 "$album_dir"*.opus 2>/dev/null | wc -l)
[[ $audio_count -lt 2 ]] && continue
echo ""
echo "Processing: $dir_name ($audio_count files)"
# Get first audio file for metadata
first_file=$(ls "$album_dir"*.flac "$album_dir"*.mp3 2>/dev/null | head -1)
[[ -z "$first_file" ]] && continue
# Extract metadata
artist=$(ffprobe -v quiet -show_entries format_tags=artist -of default=noprint_wrappers=1:nokey=1 "$first_file" 2>/dev/null)
album_artist=$(ffprobe -v quiet -show_entries format_tags=album_artist -of default=noprint_wrappers=1:nokey=1 "$first_file" 2>/dev/null)
album=$(ffprobe -v quiet -show_entries format_tags=album -of default=noprint_wrappers=1:nokey=1 "$first_file" 2>/dev/null)
# Prefer album_artist over artist
[[ -n "$album_artist" ]] && artist="$album_artist"
# Clean for filesystem
artist=$(echo "$artist" | tr -d '<>:"/\\|?*' | sed 's/\.$//')
album=$(echo "$album" | tr -d '<>:"/\\|?*' | sed 's/\.$//')
if [[ -z "$artist" ]] || [[ -z "$album" ]]; then
echo " ⚠ Missing metadata, skipping"
continue
fi
dest_dir="$LIBRARY_DIR/$artist/$album"
echo " → $artist / $album"
if $DRY_RUN; then
echo " [DRY RUN] Would transcode to $dest_dir"
continue
fi
mkdir -p "$dest_dir"
# Transcode FLACs
for flac in "$album_dir"*.flac "$album_dir"*.FLAC; do
[[ ! -f "$flac" ]] && continue
base=$(basename "$flac")
# Remove .flac extension properly
name="${base%.[Ff][Ll][Aa][Cc]}"
opus_out="$dest_dir/${name}.opus"
echo " Transcoding: $name"
ffmpeg -hide_banner -loglevel error -i "$flac" \
-c:a libopus -b:a "$BITRATE" -vbr on \
-map_metadata 0 -y "$opus_out"
[[ -f "$opus_out" ]] && chgrp "$GROUP" "$opus_out" 2>/dev/null
done
# Remove any FLACs from destination (we only want Opus there)
for flac in "$dest_dir"/*.flac "$dest_dir"/*.FLAC; do
[[ -f "$flac" ]] && rm "$flac"
done
# Copy non-FLAC audio
for audio in "$album_dir"*.mp3 "$album_dir"*.m4a "$album_dir"*.ogg; do
[[ -f "$audio" ]] && cp "$audio" "$dest_dir/"
done
# Copy artwork
for art in "$album_dir"*.jpg "$album_dir"*.png "$album_dir"cover.* "$album_dir"folder.*; do
[[ -f "$art" ]] && cp "$art" "$dest_dir/"
done
# Fix permissions
chgrp -R "$GROUP" "$dest_dir" 2>/dev/null
chmod -R g+rw "$dest_dir" 2>/dev/null
# Run beets for tagging
echo " Running beets..."
beet import -q "$dest_dir" 2>/dev/null
if [[ $? -eq 0 ]]; then
echo " ✓ Beets tagging complete"
else
echo " ⚠ Beets tagging skipped or failed (check manually)"
fi
# Verify
new_count=$(ls -1 "$dest_dir"/*.opus "$dest_dir"/*.mp3 2>/dev/null | wc -l)
if [[ $new_count -ge 3 ]]; then
echo " ✓ Imported $new_count files"
rm -rf "$INGEST_DIR/$dir_name"
echo " ✓ Cleaned source"
else
echo " ✗ Verification failed"
fi
done
echo ""
echo "=== Done ==="