Combining Two iTunes Libraries, No Duplicates Wanted.

Page content

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)