Saving The Files
Our code is already in place for moving the files to the proper location to match our file structure. All we have to do is save the ID3 data, populate the variables for building the file path, and remove a few return and print statements we were using for testing. We'll also add a printed message to notify the user of the progress the application has made.
Code:
Our code is already in place for moving the files to the proper location to match our file structure. All we have to do is save the ID3 data, populate the variables for building the file path, and remove a few return and print statements we were using for testing. We'll also add a printed message to notify the user of the progress the application has made.
Code:
#!/usr/bin/python import sys, os, fnmatch, shutil, argparse, re, urllib, urllib2, xml.dom.minidom 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() return id3info def getID3FromWeb(file, id3info): # Get the Artist and Title from id3info to make the query _artist = id3info["artist"][0] _title = id3info["title"][0] # Query the MusicBrainz web service try: query = { 'query' : 'artist:' + _artist + ' AND recording:' + _title } response = urllib2.urlopen('http://musicbrainz.org/ws/2/recording?' + urllib.urlencode(query)) x = xml.dom.minidom.parse(response) recordingList = x.getElementsByTagNameNS('http://musicbrainz.org/ns/mmd-2.0#', 'recording-list')[0] recording = recordingList.getElementsByTagName('recording')[0] if recording.nodeType == 1 and recording.attributes.get('ext:score').value == '100': id3info["tracknumber"] = getTrackNumber(str(int(recording.getElementsByTagName('track-list')[0].attributes.get('offset').value)+1)) id3info["artist"] = recording.getElementsByTagName('name')[0].firstChild.nodeValue id3info["title"] = recording.getElementsByTagName('title')[0].firstChild.nodeValue # Check for multiple releases containing the title releaseList = recording.getElementsByTagName('release-list')[0] releases = releaseList.getElementsByTagName('release') releaseOpts = [] for release in releases: releaseTitle = release.getElementsByTagName('title')[0].firstChild.nodeValue if releaseTitle not in releaseOpts: releaseOpts.append(releaseTitle) if len(releaseOpts) > 1: # Ask the user which option they'd prefer print file + " has multiple options:" index = 0 print "0) Ignore file and discard edits." for opt in releaseOpts: index += 1 print str(index) + ") " + opt choice = input("Choice: ") if choice == 0: print "You chose to ignore the file." else: id3info["album"] = releaseOpts[choice - 1] id3info["tracknumber"] = getTrackNumber(str(int(releases[choice - 1].getElementsByTagName('track-list')[0].attributes.get('offset').value)+1)) else: id3info["album"] = releaseOpts[0] id3info.save() except: print sys.exc_info()[0] return id3info 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: id3info = getID3FromFilename(file, id3info) else: id3info = getID3FromWeb(file, id3info) _trackNumber = id3info["tracknumber"][0] _artist = id3info["artist"][0] _title = id3info["title"][0] _album = id3info["album"][0] outputDir = output + "/" + _artist + "/" + _album + "/" outputFile = _trackNumber + ". " + _artist + " - " + _title + ".mp3" print "Saving file ", outputDir + outputFile 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/ Saving file /Music/Thrice/The Alchemy Index, Volumes III & IV/01. Thrice - Moving Mountains.mp3 Saving file /Music/Thrice/The Alchemy Index, Volumes III & IV/06. Thrice - Child of Dust.mp3 Saving file /Music/Thrice/The Alchemy Index, Volumes III & IV/03. Thrice - The Earth Isn't Humming.mp3 Saving file /Music/Thrice/The Alchemy Index, Volumes III & IV/02. Thrice - Digging My Own Grave.mp3 Saving file /Music/Thrice/The Alchemy Index, Volumes III & IV/04. Thrice - The Lion and the Wolf.mp3 /UntamedMusic/The Alchemy Index Vol. 4 - Earth/Thrice - Come All You Weary.mp3 has multiple options: 0) Ignore file and discard edits. 1) Come All You Weary 2) The Alchemy Index, Volumes III & IV Choice: 2 Saving file /Music/Thrice/The Alchemy Index, Volumes III & IV/05. Thrice - Come All You Weary.mp3