summaryrefslogtreecommitdiff
path: root/import_music.sh
diff options
context:
space:
mode:
authorCaine <susan@tomflux.xyz>2026-02-15 09:41:49 +0000
committerCaine <susan@tomflux.xyz>2026-02-15 09:41:49 +0000
commitc7956ae9b228054d57897ea338ad4154cc0b7221 (patch)
treea1f517ea452dddf34468c01c85871358e8b7295e /import_music.sh
Initial commit: susan automation scriptsHEADmaster
Overnight transcoding, music discovery/import, system health reports, stats page generator, and bookmark management. Secrets stored in /etc/automation/ — not in repo.
Diffstat (limited to 'import_music.sh')
-rwxr-xr-ximport_music.sh131
1 files changed, 131 insertions, 0 deletions
diff --git a/import_music.sh b/import_music.sh
new file mode 100755
index 0000000..bf6c1aa
--- /dev/null
+++ b/import_music.sh
@@ -0,0 +1,131 @@
+#!/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 ==="