def OnDetectCD(self, e): """ this is what runs when the "Detect CD" button is clicked (believe it or not). First we try to identify the CD using the musicbrainz library, then cddb, if that fails, then give up. """ if self.cmdDetectCD.GetValue(): self.goToWorkDir() if self.cddevice: wx.LogMessage("using CD device %s" % self.cddevice) wx.LogMessage("Checking musicbrainz for tags") wx.LogMessage("For information, see http://www.musicbrainz.org") info = None try: info = self._lookup_mb() except mbdisc.DiscError, e: wx.LogMessage("can't read disc: %s" % e) self.cmdDetectCD.SetValue(False) return if info == None: wx.LogMessage("musicbrainz failed, trying cddb") try: info = self._lookup_cddb() except: info = None if info == None: # nowhere else to look wx.LogMessage("nothing in cddb either: no tags available") info = cdinfo() # try to use musicbrainz library to at least find out the # number of tracks disc = mbdisc.readDisc(self.cddevice) for i in range(disc.firstTrackNum, disc.lastTrackNum + 1): info.titles[i] = "" # get cover art from Amazon if info.artist and info.album: wx.LogMessage("searching Amazon for cover art") urls = None try: info.releasedate, urls = amazon.LookupDisc( info.artist, info.album) except amazon.NoAmazonKeys: wx.LogMessage("not configured with amazon keys; skipping") # urls is a list, but we only bother with the first entry if urls: data = urllib.urlopen(urls[0]) ext = os.path.splitext(urls[0])[1] fd, imgfile = tempfile.mkstemp(ext, "amazon.", self.workdir) os.write(fd, data.read()) os.close(fd) self.setCoverArt(imgfile) else: wx.LogMessage("no results from Amazon search") # fill out tags box self.txtTags.SetValue(info.convertToTags())
def __init__(self, device): self.device = device try: self.cd = disc.readDisc(device) except: self.disc_id = None else: self.disc_id = self.cd.getId()
def OnDetectCD(self, e): """ this is what runs when the "Detect CD" button is clicked (believe it or not). First we try to identify the CD using the musicbrainz library, then cddb, if that fails, then give up. """ if self.cmdDetectCD.GetValue(): self.goToWorkDir() if self.cddevice: wx.LogMessage("using CD device %s" % self.cddevice) wx.LogMessage("Checking musicbrainz for tags") wx.LogMessage("For information, see http://www.musicbrainz.org") info = None try: info = self._lookup_mb() except mbdisc.DiscError, e: wx.LogMessage("can't read disc: %s" % e) self.cmdDetectCD.SetValue(False) return if info == None: wx.LogMessage("musicbrainz failed, trying cddb") try: info = self._lookup_cddb() except: info = None if info == None: # nowhere else to look wx.LogMessage("nothing in cddb either: no tags available") info = cdinfo() # try to use musicbrainz library to at least find out the # number of tracks disc = mbdisc.readDisc(self.cddevice) for i in range(disc.firstTrackNum, disc.lastTrackNum + 1): info.titles[i] = "" # get cover art from Amazon if info.artist and info.album: wx.LogMessage("searching Amazon for cover art") urls = None try: info.releasedate, urls = amazon.LookupDisc(info.artist, info.album) except amazon.NoAmazonKeys: wx.LogMessage("not configured with amazon keys; skipping") # urls is a list, but we only bother with the first entry if urls: data = urllib.urlopen(urls[0]) ext = os.path.splitext(urls[0])[1] fd, imgfile = tempfile.mkstemp(ext, "amazon.", self.workdir) os.write(fd, data.read()) os.close(fd) self.setCoverArt(imgfile) else: wx.LogMessage("no results from Amazon search") # fill out tags box self.txtTags.SetValue(info.convertToTags())
def _create_cue(self): self.goToWorkDir() info = cdinfo() info.parseFromTags(self.txtTags.GetValue()) disc = mbdisc.readDisc(self.cddevice) # note that we had cdparanoia rip from track 1 to end of disc, so # we actually dropped the pregap to track 1 (which cdparanoia calls # track 0) in the rip (TO DO: is this a problem?). So frame 0 of our # cdparanoia output is actually frame 0 of track 1. Therefore for # our cuesheet to line up, we have to find out how long the dropped # pregap was (almost always 2 seconds as per CDDA standard) and # subtract that many frames from all of the track boundaries. # # Just to make this more fun, musicbrainz uses 1-based numbering # for tracks, so cdparanoia's track 1 is mb's track 2, etc. ##pregapoffset = int(mb.GetResultData1(MBE_TOCGetTrackSectorOffset, 2)) cue = open("cue", "w") cue.write('FILE "dummy.wav" WAVE\n') # The cuesheet specification allows TITLE and PERFORMER tags for # purposes of CD-TEXT, but there is no point generating them since # FLAC stores cuesheets in its own representation that preserves only # the seek points # cue.write(' TITLE "') # cue.write(info.album) # cue.write('"\n') # cue.write(' PERFORMER "') # cue.write(info.artist) # cue.write('"\n') pregapframes = disc.tracks[0][0] i = disc.firstTrackNum for (offset, length) in disc.tracks: cue.write(" TRACK %02d AUDIO\n" % i) offset -= pregapframes # It appears from the cdda specifications that: # 1 CD-ROM sector == 2352 bytes == 1 frame == 1/75 sec CDDA (secs, frames) = divmod(offset, 75) (mins, secs) = divmod(secs, 60) ## if i in info.titles: ## cue.write(' TITLE "') ## cue.write(info.titles[i]) ## cue.write('"\n') cue.write(" INDEX 01 %02d:%02d:%02d\n" % (mins, secs, frames)) i += 1 cue.close() return
def _create_cue(self): self.goToWorkDir() info = cdinfo() info.parseFromTags(self.txtTags.GetValue()) disc = mbdisc.readDisc(self.cddevice) # note that we had cdparanoia rip from track 1 to end of disc, so # we actually dropped the pregap to track 1 (which cdparanoia calls # track 0) in the rip (TO DO: is this a problem?). So frame 0 of our # cdparanoia output is actually frame 0 of track 1. Therefore for # our cuesheet to line up, we have to find out how long the dropped # pregap was (almost always 2 seconds as per CDDA standard) and # subtract that many frames from all of the track boundaries. # # Just to make this more fun, musicbrainz uses 1-based numbering # for tracks, so cdparanoia's track 1 is mb's track 2, etc. ##pregapoffset = int(mb.GetResultData1(MBE_TOCGetTrackSectorOffset, 2)) cue = open('cue', 'w') cue.write('FILE "dummy.wav" WAVE\n') # The cuesheet specification allows TITLE and PERFORMER tags for # purposes of CD-TEXT, but there is no point generating them since # FLAC stores cuesheets in its own representation that preserves only # the seek points #cue.write(' TITLE "') #cue.write(info.album) #cue.write('"\n') #cue.write(' PERFORMER "') #cue.write(info.artist) #cue.write('"\n') pregapframes = disc.tracks[0][0] i = disc.firstTrackNum for (offset, length) in disc.tracks: cue.write(' TRACK %02d AUDIO\n' % i) offset -= pregapframes # It appears from the cdda specifications that: # 1 CD-ROM sector == 2352 bytes == 1 frame == 1/75 sec CDDA (secs, frames) = divmod(offset, 75) (mins, secs) = divmod(secs, 60) ## if i in info.titles: ## cue.write(' TITLE "') ## cue.write(info.titles[i]) ## cue.write('"\n') cue.write(' INDEX 01 %02d:%02d:%02d\n' % (mins, secs, frames)) i += 1 cue.close() return
def read_release_from_device(device): """ Reads a L{ExtRelease} object from the given device. Reads the disc ID of the disc in the given device, then contacts the U{musicbrainz.org} website to create and return a L{ExtRelease} object. @param device: the path to the device. @return: a L{ExtRelease} object. @raise RuntimeError: if one of the following occurs: - The CD couldn't be read. - The U{musicbrainz.org} website could not be contacted. - The CD's DiscID was not available on U{musicbrainz.org}. """ disc = None try: disc = mb2disc.readDisc(deviceName=device) except mb2disc.DiscError as e: raise RuntimeError( "An error occurred while reading the CD: {}".format(e.message)) query = mb2ws.Query() releaseFilter = mb2ws.ReleaseFilter(discId=disc.getId(), limit=1) releaseResults = None try: releaseResults = query.getReleases(filter=releaseFilter) except (mb2ws.ConnectionError, mb2ws.RequestError, mb2ws.ResponseError) as e: raise RuntimeError( "An error occurred while trying to retrieve the track information: {}" .format(e.message)) if len(releaseResults) < 1: raise RuntimeError( "Couldn't find DiscID {} in musicbrainz.org database.\n" "If you'd like to add it, use this link:\n {}".format( disc.getId(), mb2disc.getSubmissionUrl(disc))) release = releaseResults[0].getRelease() release.__class__ = ExtRelease release.setDefaults() return release
def from_musicbrainz(self): disc = mbdisc.readDisc() filter_ = mbws.ReleaseFilter(discId=disc.getId()) service = mbws.WebService() query = mbws.Query(service) results = query.getReleases(filter_) self.genre = '' #musicbrainz does not support genres self.artist = results[0].release.artist.name self.album = results[0].release.getTitle() try: self.year = results[0].release.getEarliestReleaseDate( ).split('-')[0] except AttributeError: LOGGER.warning( "Couldn't' get year from MusicBrainz - using current year") self.year = str(datetime.datetime.now().year) self.titles = [ track.getTitle() for track in results[0].release.getTracks()] return self
def _lookup_mb(self): """ throws an exception if the CD can't be read, returns None if the CD is ok but musicbrainz has nothing for us. """ disc = mbdisc.readDisc(self.cddevice) # exception here if no cd try: # search by disc ID svc = mbws.WebService() q = mbws.Query(svc) matches = q.getReleases(mbws.ReleaseFilter(discId=disc.getId())) if len(matches) == 0: wx.LogMessage('no results from musicbrains') return None # go back for full details on the first match wx.LogMessage('musicbrains found %d matches' % len(matches)) release = matches[0].release #inc = mbws.ReleaseIncludes(artist=True, tracks=True, # releaseEvents=True) #release = q.getReleaseById(release.getId(), inc) isOneArtist = release.isSingleArtistRelease() ret = cdinfo() ret.artist = release.artist.getUniqueName() ret.album = release.title # using the dictionary is gratuitous for musicbrainz, which # returns the titles in order, but cddb does not. i = 1 for t in release.tracks: if isOneArtist or t.artist is None: ret.titles[i] = t.title else: ret.titles[i] = '%s-%s' % (t.artist.name, t.title) i += 1 except mbws.WebServiceError, e: wx.LogMessage('musicbrainz error: %s' % e) return None
def _lookup_mb(self): """ throws an exception if the CD can't be read, returns None if the CD is ok but musicbrainz has nothing for us. """ disc = mbdisc.readDisc(self.cddevice) # exception here if no cd try: # search by disc ID svc = mbws.WebService() q = mbws.Query(svc) matches = q.getReleases(mbws.ReleaseFilter(discId=disc.getId())) if len(matches) == 0: wx.LogMessage("no results from musicbrains") return None # go back for full details on the first match wx.LogMessage("musicbrains found %d matches" % len(matches)) release = matches[0].release # inc = mbws.ReleaseIncludes(artist=True, tracks=True, # releaseEvents=True) # release = q.getReleaseById(release.getId(), inc) isOneArtist = release.isSingleArtistRelease() ret = cdinfo() ret.artist = release.artist.getUniqueName() ret.album = release.title # using the dictionary is gratuitous for musicbrainz, which # returns the titles in order, but cddb does not. i = 1 for t in release.tracks: if isOneArtist or t.artist is None: ret.titles[i] = t.title else: ret.titles[i] = "%s-%s" % (t.artist.name, t.title) i += 1 except mbws.WebServiceError, e: wx.LogMessage("musicbrainz error: %s" % e) return None
def searchMbForDisc(device): d = disc.readDisc(device) cdid = d.id numTracks = d.getLastTrackNum() toc = {} toc["first_track"] = 1 toc["num_tracks"] = numTracks toc["length"] = d.sectors toc["offsets"] = [x for (x, y) in d.getTracks()] logging.debug("toc: %s" % str(toc)) logging.info("querying musicbrainz.org to see if this cd is on there...") q = ws.Query() rf = ws.ReleaseFilter(discId=cdid) releases = [result.getRelease() for result in q.getReleases(rf)] numFound = len(releases) if numFound == 0: return (toc, numFound, (disc.getSubmissionUrl(d), ), None) else: return (toc, numFound, (cdid, numTracks), releases)
def searchMbForDisc(device): d = disc.readDisc(device) cdid = d.id numTracks = d.getLastTrackNum() toc = {} toc["first_track"] = 1 toc["num_tracks"] = numTracks toc["length"] = d.sectors toc["offsets"] = [x for (x, y) in d.getTracks()] logging.debug("toc: %s" % str(toc)) logging.info("querying musicbrainz.org to see if this cd is on there...") q = ws.Query() rf = ws.ReleaseFilter(discId=cdid) releases = [result.getRelease() for result in q.getReleases(rf)] numFound = len(releases) if numFound == 0: return (toc, numFound, (disc.getSubmissionUrl(d),), None) else: return (toc, numFound, (cdid, numTracks), releases)
def identify(self): """Drrn drrn """ ## XXX: Do we really need the old library for this? try: disc = mbdisc.readDisc(deviceName=DEVICE) except mbdisc.DiscError as e: logger.error('[FAIL] %s' % e) logger.error(''.join(traceback.format_exception(*sys.exc_info()))) self.interface.queue_to_identify_interface.send('FAILED_IDENTIFY') return disc_id = disc.getId() submission_url = mbdisc.getSubmissionUrl(disc) logger.info('[URL] %s' % submission_url) logger.info('[SUCCESS] Identified disc as: %s' % disc_id) ## XXX: The library doesn't understand a tuple here includes = ['artist-credits', 'labels', 'release-rels', 'recordings'] try: data = musicbrainzngs.get_releases_by_discid(disc_id, includes=includes) except musicbrainzngs.ResponseError as e: ## Fake response to make flow easier if e.cause.code == 404: data = { 'disc': { 'id': disc_id, 'release-list': [] } } else: raise except Exception as e: logger.error('[FAIL] %s' % e) logger.error(''.join(traceback.format_exception(*sys.exc_info()))) self.interface.queue_to_identify_interface.send('FAILED_IDENTIFY') return else: if not data: logger.error('[FAIL] No data returned. Check submission url') return try: releases = data['disc']['release-list'] except KeyError: if 'cdstub' in data: logger.warn('[DISC] This release is only a stub, fill it in at MusicBrainz') self.interface.queue_to_identify_interface.send('NO_DATA') return # Make the chance slimmer that selecting a release breaks if mb sends results in odd orders releases.sort() logger.info('[SUCCESS] Got %d releases' % len(releases)) if len(releases) == 0: self.interface.queue_to_identify_interface.send('NO_DATA') self.interface.queue_to_identify_interface.send(submission_url) return elif len(releases) > 1: self.interface.queue_to_identify_interface.send('MULTIPLE_RELEASES') self.interface.queue_to_identify_interface.send(releases) rel_num = self.interface.queue_to_identify_interface.recv() else: rel_num = 0 ## Which release do we want? release = releases[rel_num] ## Disc title title = release['title'].encode('utf-8') disambiguation = release.get('disambiguation', None) is_remaster = False for rel_release in release.get('release-relation-list', ()): if rel_release.get('type', None) == 'remaster': is_remaster = True break extra_str = '' if disambiguation: extra_str = '%s' % disambiguation if is_remaster: extra_str = '%s remaster' % extra_str extra_str = extra_str.strip() if extra_str: title = '%s (%s)' % (title, extra_str) ## Require release date date = release['date'] if not date: self.interface.queue_to_identify_interface.send('NO_DATE') return year = date.split('-', 1)[0] ## 0th artist... if len(release['artist-credit']) > 1: self.interface.queue_to_identify_interface.send('TOO_MANY_ARTISTS') return artist_sort_name = release['artist-credit'][0]['artist']['sort-name'] artist_sort_name = artist_sort_name.encode('utf-8') ## Media count and name disc_num = 1 if 'medium-count' not in release: disc_count = len(release['medium-list']) else: disc_count = release['medium-count'] medium_n = 0 media_name = None if disc_count > 1: for medium_n, medium in enumerate(release['medium-list']): if 'title' in medium: media_name = medium['title'] else: media_name = None if disc_id in [d['id'] for d in medium['disc-list']]: disc_num = medium_n + 1 break # Pass the media_name along if required, otherwise it's None if media_name is not None and media_name == title: media_name = None ## Unlike the mb example code, disregard different track artists track_tuples = [] for track in release['medium-list'][medium_n]['track-list']: formatted_track_num = '%02d' % int(track['number']) track_title = track['recording']['title'].encode('utf-8') track_tuple = TrackTuple(disc_id=disc_id, release_id=release['id'], artist=artist_sort_name, year=year, title=title, formatted_track_num=formatted_track_num, track_title=track_title, disc_num=disc_num, disc_count=disc_count, media_name=media_name) track_tuples.append(track_tuple) self.interface.queue_to_identify_interface.send('FINISHED_IDENTIFY') self.interface.queue_to_identify_interface.send(tuple(track_tuples))
# Activate logging. # logging.basicConfig() logger = logging.getLogger() logger.setLevel(logging.DEBUG) # Setup a Query object. # service = mbws.WebService() query = mbws.Query(service) # Read the disc in the drive # try: disc = mbdisc.readDisc() except mbdisc.DiscError, e: print "Error:", e sys.exit(1) # Query for all discs matching the given DiscID. # try: filter = mbws.ReleaseFilter(discId=disc.getId()) results = query.getReleases(filter) except mbws.WebServiceError, e: print "Error:", e sys.exit(2)
def readDisc(): try: return mbdisc.readDisc() except mbdisc.DiscError, e: errQuit("Error: Cannot read disc: " + str(e))
class ConvertDisc: '''Wrapper around ConvertTrack to Convert complete cds Currently uses MusicBrainz, but a CDDB fallback will be added, too.''' def __init__(self, dev, conf): warn("Converting CDs is not well supported, please use another " "solution.") self.dev, self.conf = dev, conf try: self.get_mb() except self.MBError: warn('MusicBrainz failed. Trying FreeDB...') self.get_cddb() class MBError(Exception): '''Empty''' def get_cddb(self): try: import CDDB, DiscID except ImportError: fatal('You need python-cddb (http://cddb-py.sf.net) to convert cds. Please install it.') disc_id = DiscID.disc_id(DiscID.open(self.dev)) query_info = CDDB.query(disc_id)[1] if not query_info: fatal('The disk is not listed in FreeDB, dir2ogg only supports disk listed in MusicBrainz or FreeDB') if isinstance(query_info, list): query_info = query_info[0] read_info = CDDB.read(query_info['category'], query_info['disc_id'])[1] for track in range(disc_id[1]): title = {} title['discid'] = query_info['disc_id'] title['artist'], title['album'] = (track.strip() for track in query_info['title'].split("/")) title['genre'] = read_info['DGENRE'] title['date'] = read_info['DYEAR'] title['title'] = read_info['TTITLE' + str(track)] title['tracktotal'] = str(len(range(disc_id[1])) + 1) title['ntracknumber'] = '0' * (len(title['tracktotal'] ) - len(str(track+1)) ) + str(track+1) title['tracknumber'] = str(track+1) for key, val in title.items(): title[key] = unicode(str(val), "ISO-8859-1") ConvertTrack(self.dev, self.conf, track+1, title) def get_mb(self): try: import musicbrainz2.disc as mbdisc import musicbrainz2.webservice as mbws except ImportError, err: warn('You need python-musicbrainz2 (or python-cddb) to convert cds. Please install it. Trying cddb.') raise self.MBError, err service = mbws.WebService() query = mbws.Query(service) # Read the disc in the drive try: disc = mbdisc.readDisc(self.dev) except mbdisc.DiscError, err: warn(err) raise self.MBError
# # Read a CD in the disc drive and calculate a MusicBrainz DiscID. # # Usage: # python discid.py # # $Id: discid.py 201 2006-03-27 14:43:13Z matt $ # import sys from musicbrainz2.disc import readDisc, getSubmissionUrl, DiscError try: # Read the disc in the default disc drive. If necessary, you can pass # the 'deviceName' parameter to select a different drive. # disc = readDisc() except DiscError, e: print "DiscID calculation failed:", str(e) sys.exit(1) print 'DiscID :', disc.id print 'First Track :', disc.firstTrackNum print 'Last Track :', disc.lastTrackNum print 'Length :', disc.sectors, 'sectors' i = disc.firstTrackNum for (offset, length) in disc.tracks: print "Track %-2d : %8d %8d" % (i, offset, length) i += 1 print 'Submit via :', getSubmissionUrl(disc)
def get_mb(self): try: import musicbrainz2.disc as mbdisc import musicbrainz2.webservice as mbws except ImportError as err: warn( 'You need python-musicbrainz2 (or python-cddb) to convert cds. Please install it. Trying cddb.' ) raise self.MBError(err) service = mbws.WebService() query = mbws.Query(service) # Read the disc in the drive try: disc = mbdisc.readDisc(self.dev) except mbdisc.DiscError as err: warn(err) raise self.MBError discId = disc.getId() try: myfilter = mbws.ReleaseFilter(discId=discId) results = query.getReleases(myfilter) except mbws.WebServiceError as err: warn(err) raise self.MBError if len(results) == 0: print("Disc is not yet in the MusicBrainz database.") print("Consider adding it via", mbdisc.getSubmissionUrl(disc)) raise self.MBError try: inc = mbws.ReleaseIncludes(artist=True, tracks=True, releaseEvents=True) release = query.getReleaseById(results[0].release.getId(), inc) except mbws.WebServiceError as err: warn(err) raise self.MBError isSingleArtist = release.isSingleArtistRelease() try: # try to get the CDDB ID import DiscID cddb_id = '%08lx' % int(DiscID.disc_id(DiscID.open(self.dev))[0]) except: cddb_id = False trackn = 1 for track in release.tracks: title = {} title[ 'artist'] = isSingleArtist and release.artist.name or track.artist if cddb_id: title['discid'] = cddb_id title['album'] = release.title title['date'] = release.getEarliestReleaseDate() title['musicbrainz_albumartistid'] = release.artist.id.split( "/")[-1] title['musicbrainz_albumid'] = release.id.split("/")[-1] title['musicbrainz_discid'] = discId title['musicbrainz_sortname'] = release.artist.sortName title['musicbrainz_trackid'] = track.id.split("/")[-1] title['title'] = track.title title['tracktotal'] = str(len(release.tracks)) title['ntracknumber'] = "%02d" % trackn title['tracknumber'] = str(trackn) ConvertTrack(self.dev, self.conf, trackn, title) trackn += 1
def get_disc(self): self.ui.status('Reading %s TOC...' % self.device) return disc.readDisc(self.device)