Taming Your Music Collection: ID3 From Web

Distinguish The Files
     The first task on our plan is distinguishing files that are missing ID3 data and also do not follow our predefined file structure pattern. We'll use the code from the previous article to check for missing ID3 data, as well as our previously devised comparison that checks if the input directory and the output directory are the same. However, rather than checking if the directories match, we will use an else condition to determine the directories do not match, which would mean the file does not necessarily follow our predefined pattern.
Code:
#!/usr/bin/python

import sys, os, fnmatch, shutil, argparse, re
from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3, TIT2

def getTrackNumber(track):
    if track.find("/") > -1:
        return getTrackNumber(track[0:track.find("/")])
    elif track.isdigit():
        return track.zfill(2)

def getID3FromFilename(file, id3info):
    filename = os.path.basename(file)
    m = re.match(r"(?P<trackNumber>\d{2})\. (?P<artist>((?! -).)+) - (?P<title>[^\.]+)\.mp3", filename)

    id3info["tracknumber"] = m.group('trackNumber')
    id3info["artist"] = m.group('artist')
    id3info["title"] = m.group('title')
    id3info["album"] = file.split('/')[-2]

    id3info.save()

def move(output, file, dirMatch):
    # Check if there are ID3 tags to begin with, if not, it will complain
    try:
        tag = ID3(file)
    except:
        tag = ID3()
        tag.add(TIT2(encoding=3, text=["Title"]))
        tag.save(file)

    id3info = EasyID3(file)

    try:
        _trackNumber = getTrackNumber(id3info["tracknumber"][0])
        _artist = id3info["artist"][0]
        _title = id3info["title"][0]
        _album = id3info["album"][0]
    except KeyError:
        if dirMatch:
            getID3FromFilename(file, id3info)
            return
        else:
            print "ID3 info missing and directories do not match for file: ", file
            return

    outputDir = output + "/" + _artist + "/" + _album + "/"
    outputFile = _trackNumber + ". " + _artist + " - " + _title + ".mp3"

    if not os.path.exists(outputDir):
        os.makedirs(outputDir)

    shutil.move(file, outputDir + outputFile)

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument('-d', '--directory', nargs=1, required=True, help='')
    parser.add_argument('-o', '--output', nargs=1, required=True, help='')

    args = parser.parse_args()

    directory = os.path.abspath(args.directory[0])
    output = os.path.abspath(args.output[0])

    dirMatch = (directory == output)

    for root, subFolders, filenames in os.walk(directory):
        for filename in fnmatch.filter(filenames, '*.mp3'):
            move(output, os.path.join(root, filename), dirMatch)

main()
Result:
$ ./mp3-tagger.py -d /UntamedMusic/ -o /Music/
ID3 info missing and directories do not match for file:  /UntamedMusic/The Alchemy Index Vol. 4 - Earth/Thrice - Moving Mountains.mp3
ID3 info missing and directories do not match for file:  /UntamedMusic/The Alchemy Index Vol. 4 - Earth/Thrice - Child Of Dust.mp3
ID3 info missing and directories do not match for file:  /UntamedMusic/The Alchemy Index Vol. 4 - Earth/Thrice - The Earth Isnt Humming.mp3
ID3 info missing and directories do not match for file:  /UntamedMusic/The Alchemy Index Vol. 4 - Earth/Thrice - Digging My Own Grave.mp3
ID3 info missing and directories do not match for file:  /UntamedMusic/The Alchemy Index Vol. 4 - Earth/Thrice - The Lion And The Wolf.mp3
ID3 info missing and directories do not match for file:  /UntamedMusic/The Alchemy Index Vol. 4 - Earth/Thrice - Come All You Weary.mp3
     As you can see, the files I'm working with are not in the predefined pattern, so the ID3 data cannot be determined by file path alone. You can also see from the results that at least some of the ID3 info is missing. The files I'm using to test have the Artist and Title tags filled in only.



; ;