Combining Two iTunes Libraries, No Duplicates Wanted.

I needed to merge my wifes iTunes Library with mine, and decided to write a python script to handle it for me. My main requirement was to not create duplicates, and copy to my library only the music that was exclusive to her library. Basically, copy from hers what I didn’t have. This script should work fine on any two directories with music files. It will simply look for music based on song-title, artist and album info, not by file name or size. This script will not modify either library, but simply creates a third library that contains the difference between the two. No warranty or guarantee implied! I simply stopped coding when this worked for me. I’m simply sharing in case it’s interesting or helpful to someone else. Copy this code, comment on it, or ignore it as you like!

import os
from mutagen.easyid3 import EasyID3
from mutagen.easymp4 import EasyMP4
from mutagen.id3 import ID3NoHeaderError
import traceback
import shutil


def getmlib(rootDir, report = False, log = False):
    music_dict = dict()
    failures = list()
    noid3headers = list()
    duplicate_files = list()

    for dirName, subdirList, fileList in os.walk(rootDir):
        #print('Found directory: %s' % dirName)
        for fname in fileList:
            spath = dirName + "/" + fname
            audio = None
            stitle = ''
            salbum = ''
            sbitrate = 0
            slength = 0
            sartist = ''
            try:
                if 'm4a' in fname:
                    audio = EasyMP4(spath)
                else:
                    audio = EasyID3(spath)
                #print '[debug] keys ' + str(audio.valid_keys.keys())
                if audio.has_key('title'):
                    stitle = audio['title'][0]
                if audio.has_key('artist'):
                    sartist = audio['artist'][0]
                if audio.has_key('album'):
                    salbum = audio['album'][0]
                skey = stitle + '::' + sartist + '::' + salbum
                if music_dict.has_key(skey):
                    duplicate_files.append(spath)
                music_dict[skey] = {'bitrate': sbitrate, 'artist': sartist, 'title': stitle, 'album': salbum, 'file': fname, 'path': dirName}
            except ID3NoHeaderError as nm:
                noid3headers.append(spath)
            except Exception as e:
                failures.append({spath: "UNKNOWN FAILURE: \n" + traceback.format_exc()})

    if report:
        print '[NOID3HEADERS]' + str(len(noid3headers))
        print '[UNKNOWN FAILURES]' + str(len(failures))
        print '[INFO] Found [%i] songs' % len(music_dict)
        print '[INFO] Duplicate count is %i' % len(duplicate_files)

    if log:
        noidf = open('lib-noid3headers.log', 'w')
        for file in noid3headers:
            noidf.write(file + '\n')
        noidf.close()

        dupesf = open('lib-duplicates.log','w')
        for file in duplicate_files:
            dupesf.write(file + '\n')
        dupesf.close()

    return music_dict

def getdifflib(core_lib_dir, alt_lib_dir):
    core_lib = getmlib(core_lib_dir, report=True, log=True)
    alt_lib = getmlib(alt_lib_dir, report=True, log=False)
    diff_lib = dict()
    for song_key in alt_lib.keys():
        if not core_lib.has_key(song_key):
            diff_lib[song_key] = alt_lib[song_key]
    return diff_lib

def makedifflib(diff_lib, diff_lib_dir):
    for song_key in diff_lib:
        song = diff_lib[song_key]
        artist = song['artist']
        album = song['album']
        file = song['file']
        orig_path = song['path']
        new_dir = diff_lib_dir + '/' + artist + '/' + album
        if not os.path.exists(new_dir):
            os.makedirs(new_dir)
        try:
            shutil.copy(orig_path + '/' + file, new_dir)
        except:
            print '[COPY FAIL] trying to copy '
            print orig_path
            print file
            print new_dir

if __name__ == '__main__':
    core_lib_dir = '[PATHTO]/Music/iTunes/iTunes Media/Music/'
    alt_lib_dir = "[PATHTO]/altmusic/"
    diff_lib_dir = "[PATHTO]/diffmusic/"
    #core_lib = getmlib(core_lib_dir, report = True, log = True)
    diff_lib = getdifflib(alt_lib_dir, core_lib_dir)
    makedifflib(diff_lib, diff_lib_dir)