0 reply
0 recast
0 reaction
2 replies
1 recast
6 reactions
1 reply
0 recast
3 reactions

chatgpt Below is a “bare-bones” recipe that removes Hazel, Docker, and even a GUI.
You end up with one plain Bash file plus a tiny launchd agent—nothing else.
Copy-paste, adjust the paths, and you’re done.
⸻
0 Prerequisites (one-time)
# Homebrew installs
brew install ffmpeg rclone fswatch beets # beets optional, see step 1
• ffmpeg – converts FLAC → ALAC (-acodec alac) losslessly.
• rclone – talks to Proton Drive (run rclone config once).
• fswatch – light wrapper around FSEvents; pegged at < 1 MB RAM.
• beets – only if you still want automatic MusicBrainz tagging.
⸻
1 The shell script (album-pipeline.sh)
#!/usr/bin/env bash
set -euo pipefail
### CONFIG #############################################################
INBOX="$HOME/Music/IncomingFLAC"
PROTON_REMOTE="proton:MusicBackups"
JELLYFIN_DIR="$HOME/Music/ALAC"
TMPDIR="$(mktemp -d)"
#######################################################################
# fswatch gives us the *file* that triggered the event. We want its folder.
album_dir=$(dirname "$1")
# 1. Metadata (optional) — let Jellyfin handle it, or uncomment beets:
# beet import -q "$album_dir"
# 2. Zip a backup and upload
zip_file="${TMPDIR}/$(basename "$album_dir").zip"
(cd "$album_dir" && zip -qr "$zip_file" .)
rclone move "$zip_file" "${PROTON_REMOTE}/"
# 3. Convert FLAC → ALAC
find "$album_dir" -name '*.flac' -print0 | while IFS= read -r -d '' f; do
ffmpeg -loglevel error -i "$f" -acodec alac -map_metadata 0 \
"${f%.flac}.m4a"
done
# 4. Move ALAC files to Jellyfin watch folder
dest="$JELLYFIN_DIR/$(basename "$album_dir")"
mkdir -p "$dest"
mv "$album_dir"/*.m4a "$dest/"
# 5. Optional clean-up (comment out if you want to keep originals)
rm -rf "$album_dir"
Make it executable: chmod +x album-pipeline.sh
⸻
2 The folder-watcher (com.user.album-pipeline.plist)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key> <string>com.user.album-pipeline</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fswatch</string>
<string>-0</string> <!-- NUL-delimited output -->
<string>-e</string><string>.*</string>
<string>-i</string><string>\.flac$</string>
<string>"$HOME/Music/IncomingFLAC"</string>
<string>|</string>
<string>xargs</string><string>-0</string>
<string>/path/to/album-pipeline.sh</string>
</array>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>StandardErrorPath</key><string>/tmp/album-pipeline.log</string>
<key>StandardOutPath</key> <string>/tmp/album-pipeline.log</string>
</dict>
</plist>
• Save it in ~/Library/LaunchAgents/,
• launchctl load ~/Library/LaunchAgents/com.user.album-pipeline.plist
That’s it—every new FLAC dropped in IncomingFLAC triggers the pipeline.
⸻
Why this is “simplest”
Layer What we kept What we ditched
Trigger fswatch + launchd (native) Hazel rules, Docker sidecars
Logic ~45 lines of Bash Full SwiftUI menubar app
Tools ffmpeg, zip, rclone (all CLI) TagLibKit, Swift packages, UI code
The whole stack is transparent, debuggable with echo, and fully portable to any other Mac (copy two files and run brew bundle).
If you later crave a prettier UI, wrap the same script in Shortcuts or a minimal SwiftUI front-end—but you don’t have to. 1 reply
0 recast
1 reaction