More on Apple Music and USB Sticks

March 7, 2023 at 8 PM

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 --norsrc was 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.

Related Posts