Copying Files From an iTunes Playlist to a Folder

Comments

A quick and dirty Node script to copy the files in an iTunes playlist to a folder, in random order and without preserving file names. I use it to copy music to my flashcard to listen to it in non-Apple hardware.

Getting the playlist

I generate my playlist in iTunes, then export them as XML by right clicking on the playlist name and selecting “Export…” from the contextual menu. At the end of this step I have an XML file which looks like

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Major Version</key><integer>1</integer>
    <key>Minor Version</key><integer>1</integer>
    <key>Date</key><date>2013-07-14T09:36:36Z</date>
    <key>Application Version</key><string>11.0.4</string>
    <key>Features</key><integer>5</integer>
    <key>Music Folder</key><string>file://localhost/Volumes/HD1T2/Music/</string>
    <key>Library Persistent ID</key><string>C326B89DCF4457DF</string>
    <key>Tracks</key>
    <dict>
        <key>41540</key>
        <dict>
            <key>Track ID</key><integer>41540</integer>
            <key>Name</key><string>World</string>
            <key>Artist</key><string>1619 FB</string>
            <key>Album</key><string>Sound Of Funk Vol 2</string>
            <key>Grouping</key><string>where:us</string>
            <key>Genre</key><string>funk-classic</string>
            <key>Kind</key><string>MPEG audio file</string>
            <key>Size</key><integer>6516189</integer>
            <key>Total Time</key><integer>162847</integer>
            <key>Track Number</key><integer>14</integer>
            <key>Track Count</key><integer>19</integer>
            <key>Date Modified</key><date>2010-05-15T20:40:41Z</date>
            <key>Date Added</key><date>2012-07-14T07:42:39Z</date>
            <key>Bit Rate</key><integer>320</integer>
            <key>Sample Rate</key><integer>44100</integer>
            <key>Play Count</key><integer>4</integer>
            <key>Play Date</key><integer>3330533910</integer>
            <key>Play Date UTC</key><date>2009-07-15T18:18:30Z</date>
            <key>Rating</key><integer>60</integer>
            <key>Album Rating</key><integer>60</integer>
            <key>Album Rating Computed</key><true/>
            <key>Normalization</key><integer>2644</integer>
            <key>Persistent ID</key><string>B16D68918FB4215E</string>
            <key>Track Type</key><string>File</string>
            <key>File Type</key><integer>1297106739</integer>
            <key>Location</key><string>file://localhost/Volumes/HD1T2/Music/Music/1619%20FB/Sound%20Of%20Funk%20Vol%202/14%20World.mp3</string>
            <key>File Folder Count</key><integer>5</integer>
            <key>Library Folder Count</key><integer>1</integer>
        </dict>

Filtering the file names from the iTunes xml export

I am only interested in files, so I switch to Terminal to extract the lines with the file paths from the XML document, and discard everything else. I use ag and sed for that.

ag
ag is faster than ack, which is faster than grep. It lets you find strings inside files, and it’s dead easy to install.
“Location</key><string>file://”
this is the XML fragment before each file path. I put “location” because there are other paths.
|
pipe, like always in Unix, get the output of what’s on the left and feed it as input to what’s on the right
sed
stream editor, another comand line tool that web developers don’t use enough IMHO. It comes by default with all Unix systems, including OS X. You give it (a) file(s), it applies text manipulations to it end you can save the results in another file. The manipulation themselves can be saved in a file…
-f itunes.sed
…which is exactly what I am doing here, so I can reuse it later (see below)
> /Users/ME/Desktop/export_sed.xml
this is where the output goes

The regular expression used is in a file (it makes developing it easier). The path to my music folder is hard coded. Note that regular expressions in the OS X version of sed are pretty basic, you don’t have “+” for example.

After that I am left a text file with a list of file paths in iTunes weird URL encoding variant.

1
2
3
4
5
6
7
/Volumes/HD1T2/Music/Music/1619%20FB/Sound%20Of%20Funk%20Vol%202/14%20World.mp3
/Volumes/HD1T2/Music/Music/4%20Hero%20+%20Carina%20Andersson/Creating%20Patterns/14%20Les%20Fleur.mp3
/Volumes/HD1T2/Music/Music/4%20Hero/Two%20Pages/1-07%20Spirits%20In%20Transit.mp3
/Volumes/HD1T2/Music/Music/4%20Hero/Two%20Pages/1-09%20Star%20Chasers.mp3
/Volumes/HD1T2/Music/Music/4%20Hero/Two%20Pages%20Reinterpretations/01%20Planetaria%20(Hefner%20Remix).mp3
/Volumes/HD1T2/Music/Music/A%20Guy%20Called%20Gerald/Essence/15%20Landed.mp3
...

Randomizing the list with node

I run the node script music_from_itunes from Terminal

1
2
3
cd /path/to
chmod 775 music_from_itunes
./music_from_itunes

I tried using the lazy modules to read the file as a stream instead of gobbling it up into an array in one go, but something is odd with my npm installation, and I didn’t have time to work out what. So I wrote it using only standard modules, plus one of mine. It will load the complete list in memory, shuffle it, and then print out a bash file to copy these shuffled files to an external hard disk. Note that all paths are hard coded at the top of the file.

The mandoline_helper module I wrote is a container for all my iTunes related scripts, it’s all rough and ready stuff focusing on functional style js.

At the end of this step I am left with a bash file, which is a collection of copy instructions.

1
2
3
4
5
6
cp "/Volumes/HD1T2/Music/Music/James Brown _ JB's/Hell/My Thang.mp3" "/Volumes/SD/MUSIC/0001.mp3"
cp "/Volumes/HD1T2/Music/Music/Cham/DJ Zhao_ Balmyard Rockers Mix/Funny Man.mp3" "/Volumes/SD/MUSIC/0002.mp3"
cp "/Volumes/HD1T2/Music/Music/Last Step/Sacred Symbols of Mu CD2/10 Lives With Angel.mp3" "/Volumes/SD/MUSIC/0003.mp3"
cp "/Volumes/HD1T2/Music/Music/Smokeringz/Professor Tsungs Art Of Science Funktion/10 Flytronix.mp3" "/Volumes/SD/MUSIC/0004.mp3"
cp "/Volumes/HD1T2/Music/Music/Compilations/Hip Hop Don't Stop - The Greatest/19 Egg Trippin'.mp3" "/Volumes/SD/MUSIC/0005.mp3"
...

Doing the actual copy

The output of the last step is a file in my home directory called bash_files.sh. I simply run that, and roughly one hour later (for a 16GB flash drive) it’s all done.

1
2
cd
./bash_files.sh

It (mostly) works

I run it on playlists of up to 5000 items and it worked ok, with only 1% of files that couldn’t be copied (weird character encoding introduced by iTunes that couldn’t be decoded easily). I can live with that.

Obviously there are lots of possible improvements, but I only had a window of a couple of hours and it was great just to get something that did the job.

Comments