Managing the iTunes Library

I love my iPod, as do my family love theirs, but the thing that drives me nuts about iTunes is the inability to manage your library in a way you would expect. If I add/remove songs or movies to/from my library folder, iTunes doesn’t pick up on it. When you delete a song from within iTunes it’s not removed from disk, I know this has to do that I don’t let iTunes manage my folder, but I don’t want iTunes to manage my folder, I’ll do that myself.

I run iTunes on a Windows machine and had only a solution for one of the two problems.

Problem: Update the library with the contents of the folder
To update the library with the contents of the library folder there has been program for a couple of year now and it works perfectly. It’s the iTunes Library Updater. You just tell the program where you keep your songs, select which options you would like and when you tell it to update iTunes it will start it up and make of the necessary changes. It will add new songs and it will get rid of the entries in iTunes with that exclamation mark, stating it can’t find that file.
I don’t know if a program exists that can do this for the Mac, if you know of one feel free to mention it in the comments.

Problem: Updating the contents of the folder with the library entries.
The bigger problem is when you delete files using the iTunes interface.
When you delete a file, it’s actually only deleted from the library and not from disk and so next time you run the iTunes Library Updater the songs are added back to the library. There is a solution, it’s a Python script. I can’t take all the credit as I didn’t write the program I just tweaked it a bit, I honestly don’t remember where I found it. All I know it was on a Mac forum.
I run a local network, connecting Windows and Linux machines. I run the program on my Linux machine using a share to my iTunes and my music folder.
To run he script you obviously must have Python installed, it can be installed Windows and Linux, not sure if you need to install it on a Mac but Mac also has Python support. I don’t know much about installing Python on Windows and Mac.

The script will produce two files in the home directory

  • tracks.txt – All the entries found in the Library
  • extra_tracks.txt – All the files found on disk but not the Library

I use the the extra_tracks.txt to physically remove these entries from disk by using a small shell script. The reason I didn’t incorporate the deleting in the Python script is that I would like to double check if these entries really should be deleted.

The script:

#!/usr/bin/env python

import os
import os.path
import plistlib
import sys
import codecs
import urlparse
import urllib

########################################################################
### This tool reads the iTunes Music Library and produces a list of tracks that are in the iTunes Music directory but NOT in iTunes.
#### Note this script STILL has problems with Unicode file & track names
#### mount //melbourne/Music  /d2/itunesmusic -o guest,rw,iocharset=utf8,file_mode=0777,dir_mode=0777
#### mount //melbourne/iTunes /d2/itunes        -o guest,rw,iocharset=utf8,file_mode=0777,dir_mode=0777
########################################################################

#### On Windows set the value of theITunesDirectory to "C:\Documents and Settings\\My Documents\My Music\iTunes"

theITunesDirectory = '/d2/itunes'
theLibraryFile = os.path.join(theITunesDirectory, 'iTunes Music Library.xml')
theMusicPath = '/d2/itunesmusic/'
theOrigPath = '/C:/My Music/'
theOutputTracksFile = os.path.expanduser('~/tracks.txt')
theExtraTracksFile = os.path.expanduser('~/extra_tracks.txt')

########################################################################

def unquote(source):
	try:
		return urllib.unquote(str(source))
	except:
		return urllib.unquote(source)

########################################################################

try:
	from Foundation import (NSDictionary)
	d = NSDictionary.dictionaryWithContentsOfFile_(theLibraryFile)
except:
	d = plistlib.readPlist(theLibraryFile)
theTracks = d['Tracks']

########################################################################

thePaths = []

for theKey in theTracks:
	theTrack = theTracks[theKey]
	theLocation = theTrack['Location']
	theURL = urlparse.urlparse(theLocation)
	if theURL.scheme != 'file':
		print 'Skipping non-file URL', theLocation
		continue
	thePath = unquote(theURL.path)
        thePath = thePath.replace(theOrigPath,theMusicPath);
	thePaths.append(unicode(thePath, 'utf-8'))

thePaths = set(thePaths)
theLowerPaths = set([thePath.lower() for thePath in thePaths])

f = codecs.open(theOutputTracksFile, 'w', 'utf-8')
for thePath in thePaths:
	f.write(thePath + '\n')

print 'Found %d tracks' % len(thePaths)

########################################################################

print '#' * 80

theExtraPaths = []
for root, dirs, files in os.walk(theMusicPath):
	for name in files:
		file = os.path.join(root, name)
		file = unicode(file, 'utf-8')
		ext = os.path.splitext(name)[1]
		if ext in ('.m4r', '.m4v', '.jpg'):
			print 'Skipping', name
			continue
		if name[0] == '.':
			print 'Skipping', name
			continue
		if file.lower() not in theLowerPaths:
			theExtraPaths.append(file)

print 'Found %d unaccounted for files' % len(theExtraPaths)

########################################################################

f = codecs.open(theExtraTracksFile, 'w', 'utf-8')
for thePath in theExtraPaths:
	f.write(thePath + '\n')

########################################################################

Some explanations:

  • Lines 14 and 15 are the mount commands I use to mount the Windows shares on my Linux machine. The folder /d2/itunesmusic is the music folder and the /d2/itunes is the folder containing the iTunes library file.
  • Because the directories names on my Linux machine are different from the directories names on Windows, and the Windows directories are stored in the in the iTunes XML file, I need to convert the directory names. The Linux directory is stored in a variable on line 22, the Windows directory is stored in a variable on line 23 and on line 56 the Windows directory is replaced with the Linux directory.
  • For a reason unknown to me, my directory name in the iTunes database are preceded by a forward slash. (Lines 23)
  • Lines 48-60. Store the directory and file name entries from the iTunes in a list
  • Lines 73-85. Walk through the directory where the music is stored and check if it’s in the iTunes list. We’ll skip iPhone ringtones (m4r), movies (m4v) and album art (jpg).

As said earlier the files that you can delete will be in the file extra_tracks.txt and I use this small shell script to physically delete them:

#!/bin/bash
IFS=$(echo -en "\n\b")
for i in `cat extra_tracks.txt`; do
	rm $i
done
This article is filed under the category Software and has the following tags associated with it: , , , .
  • greyback

    Hey, stumbled across this script, which was exactly what I wanted. I’ve got 2GB of disk space back!

    I added the ‘.itc’ extension to the ignore list, which I think are album art files.

    Note that this script needs Python2.5 or greater.
    Great script, thank you.
    -G