blob: bf6c1aa92ee6a8f6e22683908ca66aedf276b29b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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 ==="
|