class MusicBrainzProvider: def __init__(self): self.__musicbrainz_query__ = Query() self.__escape_list__ = ["+", "-", "=", "*", "\/", "\\", "(", ")", "&", "*", "^", "%", "$", "#", "@", "!"] def FetchTrackListByArtistRelease(self, string_artist_name, string_release_name): musicbrainz_query = Query() try: # Search for all releases matching the given name. Limit the results # to the first best matches. string_release_id = "" string_release_title = "" musicbrainz_release_filter = ReleaseFilter(title=string_release_name, artistName=string_artist_name, limit=1) musicbrainz_release_results = musicbrainz_query.getReleases(musicbrainz_release_filter) if musicbrainz_release_results is not None: if len(musicbrainz_release_results) > 0: string_release_id = musicbrainz_release_results[0].getRelease().getId() string_release_title = musicbrainz_release_results[0].getRelease().getTitle() include_info = ReleaseIncludes(artist=True, tracks=True) release = self.__musicbrainz_query__.getReleaseById(string_release_id, include_info) track_list = [] for track in release.getTracks(): track_title = track.getTitle() for escape_char in self.__escape_list__: track_title = track_title.replace(escape_char, "") track_list.append(track_title) return string_release_title, track_list except WebServiceError, e: # print 'Error:', e return []
def print_release(mbid): q = Query() i = ReleaseIncludes( artist=True, tracks=True, urlRelations=True ) rel = q.getReleaseById(mbid, i) if not rel: print 'Not found :/' return print 'Title: %s' % rel.title print 'Artist: %s' % rel.artist.name print '' for no, trk in enumerate(rel.tracks): print ' %d. %s' % ((no+1), trk.title)
def guess_release(folder, files): nfiles = [] cache = musicdns.cache.MusicDNSCache() print "Fingerprinting..." for fn in files: try: puid, _ = cache.getpuid(fn, MUSICDNS_KEY) except IOError: puid = None track = filename_track_number(fn) nfiles += [(track, fn, puid)] nfiles.sort() matchrel = defaultdict(int) for i, (no, fn, puid) in enumerate(nfiles): print 'Asking MusicBrainz about ' + basename(fn) if puid is None: continue for tno, track, release in find_releases(puid): included = 0 if no == tno: matchrel[release.id] += 1 included = 1 print u' %d - %s - %s %s' % (tno, track.title, release.title, ' <=' if included else '') q = Query() i = ReleaseIncludes( artist=True, tracks=True, urlRelations=True ) releaseids = sorted(matchrel.iteritems(), key=itemgetter(1), reverse=1) if not releaseids: return None releases = None while not releases: try: releases = [(q.getReleaseById(rid, i), freq) for rid, freq in releaseids] except WebServiceError, e: print '!! WebServiceError: ', e print '!! Retry in 5 seconds...' time.sleep(5)
def show_release(): from musicbrainz2.webservice import Query, ArtistFilter, WebServiceError import string import musicbrainz2.webservice as ws import musicbrainz2.model as m import musicbrainz2.utils as u q = Query() inc = ws.ReleaseIncludes(artist=True, releaseEvents=True, labels=True, discs=True, tracks=True) rid = u'http://musicbrainz.org/release/52ec1af7-28a1-42e3-896a-0eddc8e338d5.html' release = q.getReleaseById(rid, inc) tracks = release.tracks for t in tracks: #print dir(t) minutes,seconds = divmod(t.duration/1000,60) print t.title, '-', string.join([str(minutes),':',str(seconds)],'')
def guess_release(self, folder, files): nfiles = [] print "Fingerprinting..." for fn in files: try: puid, _ = self.cache.getpuid(fn, self.musicdns_key) except IOError: puid = None track = filename_track_number(fn) nfiles += [(track, fn, puid)] nfiles.sort() matchrel = defaultdict(int) for i, (no, fn, puid) in enumerate(nfiles): print "Asking MusicBrainz about " + basename(fn) if puid is None: continue for tno, track, release in find_releases(puid): included = 0 if no == tno: matchrel[release.id] += 1 included = 1 print u" %d - %s - %s %s" % (tno, track.title, release.title, " <=" if included else "") service = get_service() q = Query(service) i = ReleaseIncludes(artist=True, tracks=True, urlRelations=True) releaseids = sorted(matchrel.iteritems(), key=itemgetter(1), reverse=1) if not releaseids: return None releases = None while not releases: try: releases = [(q.getReleaseById(rid, i), freq) for rid, freq in releaseids] except WebServiceError, e: print "!! WebServiceError: ", e print "!! Retry in 5 seconds..." time.sleep(5)
def main(self): if len(sys.argv) < 2: print "Usage:\n\t%s <archive|folder>" % sys.argv[0] sys.exit() s = os.path.abspath(sys.argv[1]) try: shutil.rmtree(".brainwash-work") except: pass try: os.mkdir(".brainwash-work") except: pass os.chdir(".brainwash-work") if os.path.isdir(s): print "Creating working copy..." shutil.copytree(s, "work") else: subprocess.call(["unp", s]) folders = find_music_folders(".") for folder in folders: music_files = myglob(folder[0], "*" + folder[1]) if len(music_files) < 2: cue_files = myglob(folder[0], "*.cue") if len(cue_files) < 1: # ask for mbid # need to write cuesheet generation code!!! # print "There is no cue file. To generate one I need the Release MBID." # mbid = raw_input('Enter MBID: ').strip() print "There is no cuesheet. please generate one yourself :P" continue else: cue_file = cue_files[0] wav_file = self.decode_to_wav(music_files[0]) subprocess.call(["bchunk", "-w", wav_file, cue_file, folder[0] + "/tmp-brainwash-"]) wav_files = myglob(folder[0], "tmp-brainwash-*.wav") music_files = [] for wav_file in wav_files: music_files += [self.encode_to_flac(wav_file)] folder[1] = "flac" # encode into a nice format if folder[1] not in formats_final: wav_files = [] if folder[1] == ".wav": wav_files = music_files[:] else: for music_file in music_files: wav_files += [self.decode_to_wav(music_file)] music_files = [] for wav_file in wav_files: music_files += [self.encode_to_flac(wav_file)] folder[1] = "flac" # take over the tags? print "Found: " + folder[0] try: mbid = file(folder[0] + "/.mbid").read().strip() print "Using existing mbid" except: release = self.guess_release(folder[0], music_files) if release is None: # ask for mbid print "Could not guess!" mbid = raw_input("Enter MBID: ").strip() else: # ask if guess is correct tmp_mbid = release.id.split("/")[-1:][0] print "Selected %s" % tmp_mbid answer = raw_input("Correct? [Y|n]").strip() if answer == "" or answer.lower() == "y": mbid = tmp_mbid else: print str(answer.lower()) mbid = raw_input("Enter MBID: ").strip() file(join(folder[0], ".mbid"), "w").write(str(mbid)) service = get_service() q = Query(service) i = ReleaseIncludes(artist=True, tracks=True, urlRelations=True, releaseEvents=True, discs=True) try: release = q.getReleaseById(mbid, i) except WebServiceError, e: print "Error: ", e continue dst = self.config["destination_dir"] year = 9999 for event in release.releaseEvents: year = min(year, int(event.date[:4])) if year == 9999: year = int(raw_input("Enter Release Year: ").strip()) release_title = self.clean_filename(release.title) if release.TYPE_SOUNDTRACK in release.getTypes(): dst += "/_soundtracks/(%s) %s/" % (year, release_title) elif not release.artist: dst += "/_va/(%s) %s/" % (year, release_title) else: sort_char = release.artist.sortName[:1].lower() sort_char = unicodedata.normalize("NFKD", sort_char).encode("ASCII", "ignore") dst += "/%s/%s/(%s) %s/" % ( sort_char, self.clean_filename(release.artist.sortName), year, release_title, ) try: os.makedirs(dst) except: raw_input("Failed creating %s! Press Anykey" % dst) file(join(dst, ".mbid"), "w").write(str(mbid)) for music_file in music_files: # fix permissions for broken archives os.chmod(music_file, (stat.S_IRUSR + stat.S_IWUSR + stat.S_IRGRP + stat.S_IROTH)) track_number = filename_track_number(music_file) track = release.tracks[track_number - 1] if folder[1] == ".mp3": # meta_file = MP3(music_file, ID3=EasyID3) meta_file = EasyID3() elif folder[1] == ".ogg": meta_file = OggVorbis(music_file) elif folder[1] == "flac": meta_file = FLAC(music_file) else: print "Uh oh, no meta-file for " + music_file # if not meta_file.tags: # meta_file.add_tags() artist = track.artist.name if track.artist else release.artist.name meta_file["title"] = track.title meta_file["artist"] = artist meta_file["album"] = release.title meta_file["tracknumber"] = str(track_number) + "/" + str(len(music_files)) meta_file["genre"] = self.get_lastfm_tags(meta_file["artist"][0]) meta_file.save(music_file) file_dst = dst + self.clean_filename( "%02d-%s - %s%s%s" % (track_number, artist, track.title, "." if folder[1] == "flac" else "", folder[1]) ) os.rename(music_file, file_dst) # cover art image_files = myglob(folder[0], "*.[jpg|png]") if len(image_files) > 1: for image_file in image_files: os.rename(image_file, dst + basename(image_file)) elif len(image_files) == 1: os.rename(image_files[0], dst + "cover." + image_files[0][-3:]) else: # try downlaod from amazon if release.asin: urllib.urlretrieve( "http://images.amazon.com/images/P/" + release.asin + ".01.LZZZZZZZ.jpg", dst + "cover.jpg" ) if os.path.getsize(dst + "cover.jpg") < 1000L: os.remove(dst + "cover.jpg") print "Done: " + dst
sys.exit(1) if len(sys.argv) > 2: artistResults = [artistResults[int(sys.argv[2])]] else: artistResults = [artistResults[0]] tracks_ids = [] for result in artistResults: artist_name = result.artist releases = [] for rtype in [m.Release.TYPE_ALBUM, m.Release.TYPE_SINGLE, m.Release.TYPE_COMPILATION, m.Release.TYPE_REMIX]: artist = q.getArtistById(artist_name.id, ArtistIncludes( releases=(m.Release.TYPE_OFFICIAL, rtype), tags=True)) releases.extend(artist.getReleases()) for release in releases: sleep(1) # respect TOS release = q.getReleaseById(release.id, ReleaseIncludes(artist = True, tracks = True)) for track in release.tracks: name = track.artist.name if track.artist else release.artist.name full_title = (name + u" — " + track.title).lower() if not full_title in tracks_ids: print name, track.title if not sys.stdout.isatty(): print >> sys.stderr, full_title tracks_ids.append(full_title) sys.stdout.flush()
def get_release(self, gid): q = Query(self.ws) inc = ReleaseIncludes(tracks=True, isrcs=True) return q.getReleaseById(gid, include=inc)
count = 0 idxfound = -1 releasesfound = [] for i in releasearr: if len(re.findall(searchgeneral,i.title)) != 0: releasesfound.append(i) if len(releasesfound) == 0 : print "No matching releases were found..." sys.exit(1) for release in releasesfound: rid = release.id try: inc = ws.ReleaseIncludes(artist=True, releaseEvents=True, labels=True, discs=True, tracks=True) release = q.getReleaseById(rid, inc) except ws.WebServiceError, e: print 'Error:', e sys.exit(1) import subprocess proc =subprocess.Popen('cat ufb_index.csv', shell = True, stdout=subprocess.PIPE) out = proc.communicate()[0] print "Tracks found on ", release.title, ":" print for track in release.tracks: searchstr = track.title targets = '[^a-zA-Z0-9]' searchstr = re.sub(targets,'.*',searchstr) searchstr = "^[^a-zA-Z0-9]" + searchstr + ".*" searchgeneral= re.compile(searchstr,re.I | re.M) searchresults= re.findall(searchgeneral, out)
class Tagger(object): def __init__(self): self.query = Query() def guess_artist_and_disc(self, files): rel = files[0] abs = os.path.normpath(os.path.join(os.getcwdu(), rel)) dir = os.path.basename(os.path.dirname(abs)) parts = re.split('\s+-\s+', dir) if len(parts) >= 2: return parts[0], parts[1] elif len(parts) == 1: return "", parts[0] else: return "", "" def find_releases(self, artist, disc_title, track_count): query_limit = 100 f = ReleaseFilter(artistName=artist, title=disc_title, limit=query_limit) results = self.query.getReleases(f) if len(results) == query_limit: print """\ Woah! the specified artist/disc names were pretty vague we weren't able to check all possible candiates. Please try to be more specific if the correct album isn't in the following list. """ releases = [] for result in results: # wrap result into our own structure release = Release(result.release, self.query) # only keep releases with correct amount of tracks if track_count < 0 or release.tracks_total == track_count: releases.append(release) releases.sort(key=lambda r: r.title) return releases def find_release_by_mbid(self, mbid, track_count): include = ReleaseIncludes(artist=True, tracks=True) try: result = self.query.getReleaseById(mbid, include) except ResourceNotFoundError: error("There is no Release with this Musicbrainz ID") release = Release(result, self.query, details_included=True) if release.tracks_total == track_count: return release else: error("Unexpected track count for '%s - %s' expected %i but was %i" % (release.artist.name, release.title, track_count, release.tracks_total)) def order_files(self, files, tracks): """Make self.files have the same order as the tracks.""" ordered_files = [] remaining_files = list(files) for track in tracks: def similarity(file): # Strip directories and extension file = os.path.splitext(os.path.basename(file))[0] file_parts = distinctive_parts(file) track_parts = distinctive_parts(track.title) + [track.number] score = 0 for part in track_parts: if part in file_parts: score += 1 file_parts.remove(part) return score most_similar = max(remaining_files, key=similarity) remaining_files.remove(most_similar) ordered_files.append(most_similar) return ordered_files def tag(self, files, release, genre=None, strip_existing_tags=False, progress=None): files_and_tracks = zip(files, release.tracks) for file, track in files_and_tracks: if strip_existing_tags: id3.delete(file) apev2.delete(file) try: tag = id3.ID3(file) except id3.ID3NoHeaderError: tag = id3.ID3() tag.add(id3.TPE1(3, track.artist.name)) tag.add(id3.TALB(3, track.release.title)) tag.add(id3.TIT2(3, track.title)) tag.add(id3.TDRC(3, track.release.earliestReleaseDate)) tag.add(id3.TRCK(3, track.number_str())) if track.release.album_artist is not None: tag.add(id3.TPE2(3, track.release.album_artist)) discset = track.release.discset if discset: disc_num = discset.number_str() tag.add(id3.TPOS(3, disc_num)) if discset.desc: tag.delall('COMM') tag.add(id3.COMM(3, text=discset.desc, desc='', lang='eng')) if genre is not None: tag.add(id3.TCON(3, genre)) tag.add(id3.UFID(owner='http://musicbrainz.org', data=track.uuid)) tag.save(file) if progress is not None: progress(file, track) def rename(self, files, release, progress=None): warnings = [] for file, track in zip(files, release.tracks): filename = "%02i. %s.mp3" % (track.number, track.title) filename = make_fs_safe(filename) new_file = os.path.join(os.path.dirname(file), filename) if new_file == file: continue if os.path.exists(new_file): w = '"%s" already exists, not overwriting.' % new_file warnings.append(w) continue os.rename(file, new_file) if progress is not None: progress(file, track) return warnings
def __init__(self, releaseId): #self.ignoreMissing(True) super(MusicBrainz2AlbumInfo, self).__init__() if isinstance(releaseId, basestring): self.__releaseId = releaseId query = Query() include = ReleaseIncludes(artist=True, tracks=True, releaseEvents=True) release = query.getReleaseById(releaseId, include) elif isinstance(releaseId, Release): release = releaseId self.__releaseId = release.id else: raise Exception( "incorrect type parameter for MusicBrainz2AlbumInfo %s" % releaseId) self.title = release.title # XXX: musicbrainz doesn't have genre info. what to do? #self.genre = 'Musicbrainz' self.artist = release.artist.name date = release.getEarliestReleaseDate() if not date: self.year = 0 else: self.year = int(date[0:4]) self.genre = '' tracks = [] number = 0 self.isVarious = False for track in release.tracks: number += 1 # XXX: get rid of the overcomplicated validation scheme that makes # for these ugly hacks class MBTrackInfo(TrackInfo): '''Represent musicbrainz track information.''' def getPlayLength(self): 'getter' return self.__playLength def setPlayLength(self, playLength): 'setter' self.__playLength = playLength def __init__(self): super(MBTrackInfo, self).__init__() playLength = property(getPlayLength, setPlayLength) track_info = MBTrackInfo() del track_info.validateProps[ track_info.validateProps.index('playLength')] track_info.number = number if track.duration: track_info.playLength = track.duration / 1000.0 else: # XXX: make sure we don't use namebinder relying on track length track_info.playLength = 0 if track.artist: track_info.artist = track.artist.name self.isVarious = True else: track_info.artist = release.artist.name track_info.title = track.title tracks.append(track_info) self.tracks = tracks