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