I have a script that cleans up a USB stick for playing music in my car, but that’s not quite enough. What I really need is a way to synchronize Apple Music with the volume, like you would do with an iPhone—or iPod, back in the day.
I started by dragging tracks from the Music app to the USB drive icon, but the app doesn’t offer a dialog asking whether we’d like to overwrite existing files; instead, I wound up with multiple copies of every file instead of even one-way synchronization. So apparently I had to do this myself, and the result is a tiny little Python package named musicsync.
I tried a few different things, including rsync, but a combination of AppleScript (to gather files from the Music app), Python (for comparing directory trees between selected songs and the destination, and for handling the synchronization looping), and OS-level file operations works really nicely.
$ musicsync --playlist "Selected for Car" /Volumes/UNTITLED Collecting songs from playlist "Selected for Car" Collected 2514 songs Playlist root directory is "~/Music/Music/Media.localized/Music/" Building playlist song tree Found 365 dirs and 2514 files Removing extra files from "/Volumes/UNTITLED/" Copying new files from "Selected for Car" to "/Volumes/UNTITLED/" Aaron Keyes/In the Living Room: 17%|██ | 2/12 [00:06<00:25, 2.60s/it]
A couple of points I found interesting while building this:
- How you ask AppleScript for a listing really matters. Do it the naive way and you’ll be waiting a while—just to get a list of about 2,500 files on an M1 MacBook Pro.
- Because my car needs a FAT32-formatted drive, rsync (and my Python code) can’t count on file timestamps to figure out whether they’ve been modified. The only reliable thing we have is the file size itself.
- Copying files with
ditto --nocache --noextattr --noqtn --norsrcwas great for working with FAT32 and ignoring resource forks and other extended attributes.
- I found some real strange stuff with how the FAT32 volume handles Unicode file names, which led to my script being unable to find and synchronize directories with diacritics. I hack around that by normalizing Unicode strings with unicodedata.