Last active
December 11, 2024 17:48
-
-
Save elfakyn/dd62a92d701b494f1a06cdd57ac6da62 to your computer and use it in GitHub Desktop.
Make a portable, lossy copy of your music library. https://elfakyn.com/music/organize-your-library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Make a portable, lossy copy of your music library. https://elfakyn.com/music/organize-your-library | |
SOURCE_DIR="/source/music/folder" | |
DEST_DIR="/destination/music/folder" | |
JOB_FILE="./conversion_jobs.txt" | |
ERROR_LOG="./conversion_errors.log" | |
> "$JOB_FILE" | |
> "$ERROR_LOG" | |
declare -a partial_files | |
convert_flac_to_opus() { | |
local file="$1" | |
local dest_opus_path="$2" | |
partial_files+=("$dest_opus_path") | |
if ! opusenc --quiet --vbr --bitrate 256 "$file" "$dest_opus_path" 2>&1 | tee -a "$ERROR_LOG"; then | |
# If conversion failed, clean up the partially written file | |
rm -f "$dest_opus_path" 2>/dev/null | |
fi | |
partial_files=("${partial_files[@]/$dest_opus_path}") | |
} | |
export -f convert_flac_to_opus | |
export ERROR_LOG | |
total_files=$(find "$SOURCE_DIR" -type f | wc -l) | |
files_processed=0 | |
files_skipped=0 | |
files_copied=0 | |
flac_files_queued=0 | |
trap 'echo -e "\nProcess interrupted. Exiting..."; clean_up_partial_files; kill 0' SIGINT | |
clean_up_partial_files() { | |
echo "Cleaning up partially written files..." | |
for file in "${partial_files[@]}"; do | |
rm -f "$file" | |
echo "Removed partial file: $file" | |
done | |
} | |
echo -n "Processing files: " | |
find "$SOURCE_DIR" -type f | while IFS= read -r file; do | |
relative_path="${file#$SOURCE_DIR/}" | |
dest_path="$DEST_DIR/$relative_path" | |
dest_folder=$(dirname "$dest_path") | |
mkdir -p "$dest_folder" | |
dest_opus_path="${dest_path%.flac}.opus" | |
if [ "${file##*.}" == "flac" ]; then | |
if [ -f "$dest_opus_path" ] && [ "$dest_opus_path" -nt "$file" ]; then | |
# Skip the file if the destination file is newer | |
files_skipped=$((files_skipped + 1)) | |
else | |
echo "convert_flac_to_opus \"$file\" \"$dest_opus_path\"" >> "$JOB_FILE" | |
flac_files_queued=$((flac_files_queued + 1)) | |
fi | |
else | |
# Check for non-FLAC files | |
if [ -f "$dest_path" ] && [ "$dest_path" -nt "$file" ]; then | |
# Skip the file if the destination file is newer | |
files_skipped=$((files_skipped + 1)) | |
else | |
partial_files+=("$dest_path") | |
cp "$file" "$dest_path" | |
partial_files=("${partial_files[@]/$dest_path}") | |
files_copied=$((files_copied + 1)) | |
fi | |
fi | |
files_processed=$((files_processed + 1)) | |
echo -ne "\rProcessing files: $files_processed/$total_files (Copied: $files_copied, Skipped: $files_skipped, FLAC queued: $flac_files_queued)" | |
done | |
echo -e "\nDone copying files." | |
echo "Running conversions in parallel..." | |
cat "$JOB_FILE" | parallel --progress --eta --bar | |
# Update m3u and m3u8 playlists to reference .opus files instead of .flac | |
find "$DEST_DIR" -type f \( -iname "*.m3u" -o -iname "*.m3u8" \) -print0 | while IFS= read -r -d '' playlist; do | |
sed -i 's/\.flac/\.opus/g' "$playlist" | |
echo "Updated playlist: $playlist" | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment