Overnight transcoding, music discovery/import, system health reports, stats page generator, and bookmark management. Secrets stored in /etc/automation/ — not in repo.
132 lines
3.9 KiB
Bash
Executable File
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 ==="
|