def itunesImport(pathtoxml): if os.path.splitext(pathtoxml)[1] == '.xml': pl = XMLLibraryParser(pathtoxml) l = Library(pl.dictionary) lst = [] for song in l.songs: lst.append(song.artist) rawlist = {}.fromkeys(lst).keys() artistlist = [f for f in rawlist if f != None] else: rawlist = os.listdir(pathtoxml) artistlist = [f for f in rawlist if f != '.DS_STORE'] for name in artistlist: time.sleep(1) artistResults = ws.Query().getArtists(ws.ArtistFilter(string.replace(name, '&', '%38'), limit=1)) for result in artistResults: time.sleep(1) artistid = u.extractUuid(result.artist.id) inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), ratings=False, releaseGroups=False) artist = ws.Query().getArtistById(artistid, inc) conn=sqlite3.connect(database) c=conn.cursor() c.execute('CREATE TABLE IF NOT EXISTS artists (ArtistID TEXT UNIQUE, ArtistName TEXT, ArtistSortName TEXT, DateAdded TEXT, Status TEXT)') c.execute('CREATE TABLE IF NOT EXISTS albums (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, ReleaseDate TEXT, DateAdded TEXT, AlbumID TEXT UNIQUE, Status TEXT)') c.execute('CREATE TABLE IF NOT EXISTS tracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration TEXT, TrackID TEXT)') c.execute('SELECT ArtistID from artists') artistlist = c.fetchall() if any(artistid in x for x in artistlist): pass else: c.execute('INSERT INTO artists VALUES( ?, ?, ?, CURRENT_DATE, ?)', (artistid, artist.name, artist.sortName, 'Active')) for release in artist.getReleases(): time.sleep(1) releaseid = u.extractUuid(release.id) inc = ws.ReleaseIncludes(artist=True, releaseEvents= True, tracks= True, releaseGroup=True) results = ws.Query().getReleaseById(releaseid, inc) for event in results.releaseEvents: if event.country == 'US': c.execute('INSERT INTO albums VALUES( ?, ?, ?, ?, ?, CURRENT_DATE, ?, ?)', (artistid, results.artist.name, results.title, results.asin, results.getEarliestReleaseDate(), u.extractUuid(results.id), 'Skipped')) conn.commit() c.execute('SELECT ReleaseDate, DateAdded from albums WHERE AlbumID="%s"' % u.extractUuid(results.id)) latestrelease = c.fetchall() if latestrelease[0][0] > latestrelease[0][1]: c.execute('UPDATE albums SET Status = "Wanted" WHERE AlbumID="%s"' % u.extractUuid(results.id)) else: pass for track in results.tracks: c.execute('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', (artistid, results.artist.name, results.title, results.asin, u.extractUuid(results.id), track.title, track.duration, u.extractUuid(track.id))) conn.commit() else: pass c.close()
def tagTrack(self, todoEntry): import eyeD3 fileName = todoEntry['mp3file'] + '.tmp' release = todoEntry['release'] track = todoEntry['track'] tag = eyeD3.Tag() tag.link(str(fileName)) # eyeD3 doesn't like unicode strings tag.header.setVersion(eyeD3.ID3_V2) if track.artist is None: tag.setArtist(release.artist.name) else: tag.setArtist(track.artist.name) tag.setTitle(track.title) tag.setAlbum(release.title) tag.setTrackNum( (todoEntry['num'], len(release.tracks)) ) types = (release.TYPE_OFFICIAL, release.TYPE_PROMOTION, release.TYPE_BOOTLEG) for t in release.types: value = extractFragment(t, NS_MMD_1) if t in types: tag.addUserTextFrame(ALBUM_TYPE, value) else: tag.addUserTextFrame(ALBUM_STATUS, value) tag.addUserTextFrame(ALBUM_ARTIST, release.artist.name) tag.addUserTextFrame(ALBUM_ARTIST_SORTNAME, release.artist.sortName) tag.addUniqueFileID(FILE_ID, str(extractUuid(track.id))) if track.artist is None: tag.addUserTextFrame(ARTIST_ID, extractUuid(release.artist.id)) else: tag.addUserTextFrame(ARTIST_ID, extractUuid(track.artist.id)) tag.addUserTextFrame(ALBUM_ID, extractUuid(release.id)) tag.addUserTextFrame(ALBUM_ARTIST_ID, extractUuid(release.artist.id)) event = release.getEarliestReleaseEvent() if event is not None: tag.addUserTextFrame(RELEASE_COUNTRY, event.country) tag.setDate(event.date[0:4]) tag.update(eyeD3.ID3_V2_3)
def importartist(artistlist): for name in artistlist: logger.log(u"Querying MusicBrainz for: "+name) time.sleep(1) artistResults = ws.Query().getArtists(ws.ArtistFilter(string.replace(name, '&', '%38'), limit=1)) for result in artistResults: if result.artist.name == 'Various Artists': logger.log(u"Top result is Various Artists. Skipping.", logger.WARNING) else: logger.log(u"Found best match: "+result.artist.name+". Gathering album information...") time.sleep(1) artistid = u.extractUuid(result.artist.id) inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), ratings=False, releaseGroups=False) artist = ws.Query().getArtistById(artistid, inc) conn=sqlite3.connect(database) c=conn.cursor() c.execute('SELECT ArtistID from artists') artistlist = c.fetchall() if any(artistid in x for x in artistlist): logger.log(result.artist.name + u" is already in the database, skipping") else: c.execute('INSERT INTO artists VALUES( ?, ?, ?, CURRENT_DATE, ?)', (artistid, artist.name, artist.sortName, 'Active')) for release in artist.getReleases(): time.sleep(1) releaseid = u.extractUuid(release.id) inc = ws.ReleaseIncludes(artist=True, releaseEvents= True, tracks= True, releaseGroup=True) results = ws.Query().getReleaseById(releaseid, inc) for event in results.releaseEvents: if event.country == 'US': c.execute('INSERT INTO albums VALUES( ?, ?, ?, ?, ?, CURRENT_DATE, ?, ?)', (artistid, results.artist.name, results.title, results.asin, results.getEarliestReleaseDate(), u.extractUuid(results.id), 'Skipped')) conn.commit() c.execute('SELECT ReleaseDate, DateAdded from albums WHERE AlbumID="%s"' % u.extractUuid(results.id)) latestrelease = c.fetchall() if latestrelease[0][0] > latestrelease[0][1]: c.execute('UPDATE albums SET Status = "Wanted" WHERE AlbumID="%s"' % u.extractUuid(results.id)) else: pass for track in results.tracks: c.execute('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', (artistid, results.artist.name, results.title, results.asin, u.extractUuid(results.id), track.title, track.duration, u.extractUuid(track.id))) conn.commit() else: logger.log(results.title + u" is not a US release. Skipping for now") c.close()
def addArtist(self, artistid): inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), ratings=False, releaseGroups=False) artist = ws.Query().getArtistById(artistid, inc) conn=sqlite3.connect(database) c=conn.cursor() c.execute('CREATE TABLE IF NOT EXISTS artists (ArtistID TEXT UNIQUE, ArtistName TEXT, ArtistSortName TEXT, DateAdded TEXT, Status TEXT)') c.execute('CREATE TABLE IF NOT EXISTS albums (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, ReleaseDate TEXT, DateAdded TEXT, AlbumID TEXT UNIQUE, Status TEXT)') c.execute('CREATE TABLE IF NOT EXISTS tracks (ArtistID TEXT, ArtistName TEXT, AlbumTitle TEXT, AlbumASIN TEXT, AlbumID TEXT, TrackTitle TEXT, TrackDuration, TrackID TEXT)') c.execute('SELECT ArtistID from artists') artistlist = c.fetchall() if any(artistid in x for x in artistlist): page = [templates._header] page.append('''%s has already been added. Go <a href="/">back</a>.''' % artist.name) logger.log(artist.name + u" is already in the database!", logger.WARNING) c.close() return page else: logger.log(u"Adding " + artist.name + " to the database.") c.execute('INSERT INTO artists VALUES( ?, ?, ?, CURRENT_DATE, ?)', (artistid, artist.name, artist.sortName, 'Active')) for release in artist.getReleases(): releaseid = u.extractUuid(release.id) inc = ws.ReleaseIncludes(artist=True, releaseEvents= True, tracks= True, releaseGroup=True) results = ws.Query().getReleaseById(releaseid, inc) time.sleep(0.6) for event in results.releaseEvents: if event.country == 'US': logger.log(u"Now adding album: " + results.title+ " to the database") c.execute('INSERT INTO albums VALUES( ?, ?, ?, ?, ?, CURRENT_DATE, ?, ?)', (artistid, results.artist.name, results.title, results.asin, results.getEarliestReleaseDate(), u.extractUuid(results.id), 'Skipped')) c.execute('SELECT ReleaseDate, DateAdded from albums WHERE AlbumID="%s"' % u.extractUuid(results.id)) latestrelease = c.fetchall() if latestrelease[0][0] > latestrelease[0][1]: logger.log(results.title + u" is an upcoming album. Setting its status to 'Wanted'...") c.execute('UPDATE albums SET Status = "Wanted" WHERE AlbumID="%s"' % u.extractUuid(results.id)) else: pass for track in results.tracks: c.execute('INSERT INTO tracks VALUES( ?, ?, ?, ?, ?, ?, ?, ?)', (artistid, results.artist.name, results.title, results.asin, u.extractUuid(results.id), track.title, track.duration, u.extractUuid(track.id))) else: logger.log(results.title + " is not a US release. Skipping it for now", logger.DEBUG) conn.commit() c.close() raise cherrypy.HTTPRedirect("/")
def getUserRating(self, entityUri): """Return the rating a user has applied to an entity. The given parameter has to be a fully qualified MusicBrainz ID, as returned by other library functions. Note that this method only works if a valid user name and password have been set. Only the rating the authenticated user applied to the entity will be returned. If username and/or password are incorrect, an AuthenticationError is raised. This method will return a L{Rating <musicbrainz2.model.Rating>} object. @param entityUri: a string containing an absolute MB ID @raise ValueError: invalid entityUri @raise ConnectionError: couldn't connect to server @raise RequestError: invalid ID or entity @raise AuthenticationError: invalid user name and/or password """ entity = mbutils.extractEntityType(entityUri) uuid = mbutils.extractUuid(entityUri, entity) params = { 'entity': entity, 'id': uuid } stream = self._ws.get('rating', '', filter=params) try: parser = MbXmlParser() result = parser.parse(stream) except ParseError, e: raise ResponseError(str(e), e)
def submitUserTags(self, entityUri, tags): """Submit folksonomy tags for an entity. Note that all previously existing tags from the authenticated user are replaced with the ones given to this method. Other users' tags are not affected. @param entityUri: a string containing an absolute MB ID @param tags: A list of either L{Tag <musicbrainz2.model.Tag>} objects or strings @raise ValueError: invalid entityUri @raise ConnectionError: couldn't connect to server @raise RequestError: invalid ID, entity or tags @raise AuthenticationError: invalid user name and/or password """ entity = mbutils.extractEntityType(entityUri) uuid = mbutils.extractUuid(entityUri, entity) params = ( ('type', 'xml'), ('entity', entity), ('id', uuid), ('tags', ','.join([unicode(tag).encode('utf-8') for tag in tags])) ) encodedStr = urllib.urlencode(params) self._ws.post('tag', '', encodedStr)
def submitUserRating(self, entityUri, rating): """Submit rating for an entity. Note that all previously existing rating from the authenticated user are replaced with the one given to this method. Other users' ratings are not affected. @param entityUri: a string containing an absolute MB ID @param rating: A L{Rating <musicbrainz2.model.Rating>} object or integer @raise ValueError: invalid entityUri @raise ConnectionError: couldn't connect to server @raise RequestError: invalid ID, entity or tags @raise AuthenticationError: invalid user name and/or password """ entity = mbutils.extractEntityType(entityUri) uuid = mbutils.extractUuid(entityUri, entity) params = ( ('type', 'xml'), ('entity', entity), ('id', uuid), ('rating', unicode(rating).encode('utf-8')) ) encodedStr = urllib.urlencode(params) self._ws.post('rating', '', encodedStr)
def submitPuids(self, tracks2puids): """Submit track to PUID mappings. The C{tracks2puids} parameter has to be a dictionary, with the keys being MusicBrainz track IDs (either as absolute URIs or in their 36 character ASCII representation) and the values being PUIDs (ASCII, 36 characters). Note that this method only works if a valid user name and password have been set. See the example in L{Query} on how to supply authentication data. @param tracks2puids: a dictionary mapping track IDs to PUIDs @raise ConnectionError: couldn't connect to server @raise RequestError: invalid track or PUIDs @raise AuthenticationError: invalid user name and/or password """ assert self._clientId is not None, 'Please supply a client ID' params = [ ] params.append( ('client', self._clientId.encode('utf-8')) ) for (trackId, puid) in tracks2puids.iteritems(): trackId = mbutils.extractUuid(trackId, 'track') params.append( ('puid', trackId + ' ' + puid) ) encodedStr = urllib.urlencode(params, True) self._ws.post('track', '', encodedStr)
def _writeTrack(self, xml, track, score=None): if track is None: return xml.start('track', { 'id': mbutils.extractUuid(track.getId()), 'ext:score': score, }) xml.elem('title', track.getTitle()) xml.elem('duration', str(track.getDuration())) self._writeArtist(xml, track.getArtist()) if len(track.getReleases()) > 0: # TODO: offset + count xml.start('release-list') for release in track.getReleases(): self._writeRelease(xml, release) xml.end() if len(track.getPuids()) > 0: xml.start('puid-list') for puid in track.getPuids(): xml.elem('puid', None, { 'id': puid }) xml.end() self._writeRelationList(xml, track) # TODO: extensions xml.end()
def submitISRCs(self, tracks2isrcs): """Submit track to ISRC mappings. The C{tracks2isrcs} parameter has to be a dictionary, with the keys being MusicBrainz track IDs (either as absolute URIs or in their 36 character ASCII representation) and the values being ISRCs (ASCII, 12 characters). Note that this method only works if a valid user name and password have been set. See the example in L{Query} on how to supply authentication data. @param tracks2isrcs: a dictionary mapping track IDs to ISRCs @raise ConnectionError: couldn't connect to server @raise RequestError: invalid track or ISRCs @raise AuthenticationError: invalid user name and/or password """ params = [ ] for (trackId, isrc) in tracks2isrcs.iteritems(): trackId = mbutils.extractUuid(trackId, 'track') params.append( ('isrc', trackId + ' ' + isrc) ) encodedStr = urllib.urlencode(params, True) self._ws.post('track', '', encodedStr)
def artist_info(artist_name): info = dict(members=None, wikipedia=None, homepage=None) # short names are bad if len(artist_name) < 3: return info q = ws.Query() filt = ws.ArtistFilter(artist_name, limit=10) results = q.getArtists(filt) results = [x for x in results if x.score >= 99] # too many high scoring hits, can't disambiguate automatically if len(results) != 1: return info artist = results[0].artist uuid = mbutils.extractUuid(artist.id) inc = ws.ArtistIncludes(artistRelations=True, urlRelations=True) artist = q.getArtistById(uuid, inc) urls = artist.getRelationTargets(m.Relation.TO_URL, m.NS_REL_1+'Wikipedia') if len(urls): info['wikipedia'] = urllib.unquote(urls[0]) urls = artist.getRelationTargets(m.Relation.TO_URL, m.NS_REL_1+'OfficialHomepage') if len(urls): info['homepage'] = urllib.unquote(urls[0]) if artist.type == m.Artist.TYPE_GROUP: members = artist.getRelations(m.Relation.TO_ARTIST, m.NS_REL_1+'MemberOfBand') addl_uri = m.NS_REL_1+'Additional' coreMembers = [r for r in members if addl_uri not in r.attributes] info['members'] = ", ".join([x.target.name for x in coreMembers if not x.endDate]) if info['members'] == "": info['members'] = None return info
def findArtist(self, name): page = [templates._header] if len(name) == 0 or name == 'Add an artist': raise cherrypy.HTTPRedirect("home") else: artistResults = ws.Query().getArtists(ws.ArtistFilter(string.replace(name, '&', '%38'), limit=8)) if len(artistResults) == 0: logger.log(u"No results found for " + name) page.append('''No results!<a class="blue" href="home">Go back</a>''') return page elif len(artistResults) > 1: page.append('''Search returned multiple artists. Click the artist you want to add:<br /><br />''') for result in artistResults: artist = result.artist detail = artist.getDisambiguation() if detail: disambiguation = '(%s)' % detail else: disambiguation = '' page.append('''<a href="addArtist?artistid=%s">%s %s</a> (<a class="externalred" href="artistInfo?artistid=%s">more info</a>)<br />''' % (u.extractUuid(artist.id), artist.name, disambiguation, u.extractUuid(artist.id))) return page else: for result in artistResults: artist = result.artist logger.log(u"Found one artist matching your search term: " + artist.name +" ("+ artist.id+")") raise cherrypy.HTTPRedirect("addArtist?artistid=%s" % u.extractUuid(artist.id))
def _writeRelation(self, xml, rel, targetType): relAttrs = ' '.join([mbutils.extractFragment(a) for a in rel.getAttributes()]) if relAttrs == '': relAttrs = None attrs = { 'type': mbutils.extractFragment(rel.getType()), 'target': mbutils.extractUuid(rel.getTargetId()), 'direction': rel.getDirection(), 'begin': rel.getBeginDate(), 'end': rel.getBeginDate(), 'attributes': relAttrs, } if rel.getTarget() is None: xml.elem('relation', attrs) else: xml.start('relation', attrs) if targetType == NS_REL_1 + 'Artist': self._writeArtist(xml, rel.getTarget()) elif targetType == NS_REL_1 + 'Release': self._writeRelease(xml, rel.getTarget()) elif targetType == NS_REL_1 + 'Track': self._writeTrack(xml, rel.getTarget()) xml.end()
def _writeLabel(self, xml, label, score=None): if label is None: return xml.start('label', { 'id': mbutils.extractUuid(label.getId()), 'type': mbutils.extractFragment(label.getType()), 'ext:score': score, }) xml.elem('name', label.getName()) xml.elem('sort-name', label.getSortName()) xml.elem('disambiguation', label.getDisambiguation()) xml.elem('life-span', None, { 'begin': label.getBeginDate(), 'end': label.getEndDate(), }) if len(label.getAliases()) > 0: xml.start('alias-list') for alias in label.getAliases(): xml.elem('alias', alias.getValue(), { 'type': alias.getType(), 'script': alias.getScript(), }) xml.end() # TODO: releases, artists self._writeRelationList(xml, label) # TODO: extensions xml.end()
def uuid_from_soup(soup, type=None): uuid_link = soup.find('a', href=MB_PATTERN) if uuid_link: try: return extractUuid(uuid_link['href'], type) except ValueError: pass # Not a valid UUID for some reason? return None
def uuid_from_soup(soup, type = None): uuid_link = soup.find('a', href=MB_PATTERN) if uuid_link: try: return extractUuid(uuid_link['href'], type) except ValueError: pass # Not a valid UUID for some reason? return None
def artistInfo(self, artistid): page = [templates._header] inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), releaseGroups=True) artist = ws.Query().getArtistById(artistid, inc) page.append('''Artist Name: %s </br> ''' % artist.name) page.append('''Unique ID: %s </br></br>Albums:<br />''' % u.extractUuid(artist.id)) for rg in artist.getReleaseGroups(): page.append('''%s <br />''' % rg.title) return page
def __init__(self, title=None, discId=None, releaseTypes=None, artistName=None, artistId=None, limit=None, offset=None, query=None, trackCount=None): """Constructor. If C{discId} or C{artistId} are set, only releases matching those IDs are returned. The C{releaseTypes} parameter allows to limit the types of the releases returned. You can set it to C{(Release.TYPE_ALBUM, Release.TYPE_OFFICIAL)}, for example, to only get officially released albums. Note that those values are connected using the I{AND} operator. MusicBrainz' support is currently very limited, so C{Release.TYPE_LIVE} and C{Release.TYPE_COMPILATION} exclude each other (see U{the documentation on release attributes <http://wiki.musicbrainz.org/AlbumAttribute>} for more information and all valid values). If both the C{artistName} and the C{artistId} parameter are given, the server will ignore C{artistName}. The C{query} parameter may contain a query in U{Lucene syntax <http://lucene.apache.org/java/docs/queryparsersyntax.html>}. Note that C{query} may not be used together with the other parameters except for C{limit} and C{offset}. @param title: a unicode string containing the release's title @param discId: a unicode string containing the DiscID @param releaseTypes: a sequence of release type URIs @param artistName: a unicode string containing the artist's name @param artistId: a unicode string containing the artist's ID @param limit: the maximum number of releases to return @param offset: start results at this zero-based offset @param query: a string containing a query in Lucene syntax @param trackCount: the number of tracks in the release @see: the constants in L{musicbrainz2.model.Release} """ if releaseTypes is None or len(releaseTypes) == 0: releaseTypesStr = None else: tmp = [ mbutils.extractFragment(x) for x in releaseTypes ] releaseTypesStr = ' '.join(tmp) self._params = [ ('title', title), ('discid', discId), ('releasetypes', releaseTypesStr), ('artist', artistName), ('artistid', mbutils.extractUuid(artistId)), ('limit', limit), ('offset', offset), ('query', query), ('count', trackCount), ] if not _paramsValid(self._params): raise ValueError('invalid combination of parameters')
def testExtractUuid(self): artistPrefix = 'http://musicbrainz.org/artist/' uuid = 'c0b2500e-0cef-4130-869d-732b23ed9df5' mbid = artistPrefix + uuid self.assertEquals(u.extractUuid(None), None) self.assertEquals(u.extractUuid(uuid), uuid) self.assertEquals(u.extractUuid(mbid), uuid) self.assertEquals(u.extractUuid(mbid, 'artist'), uuid) # not correct, but not enough data to catch this self.assertEquals(u.extractUuid(uuid, 'release'), uuid) self.assertRaises(ValueError, u.extractUuid, mbid, 'release') self.assertRaises(ValueError, u.extractUuid, mbid, 'track') self.assertRaises(ValueError, u.extractUuid, mbid+'/xy', 'artist') invalidId = 'http://example.invalid/' + uuid self.assertRaises(ValueError, u.extractUuid, invalidId)
def __init__(self, title=None, artistName=None, artistId=None, releaseTitle=None, releaseId=None, duration=None, puid=None, limit=None, offset=None, query=None): """Constructor. If C{artistId}, C{releaseId} or C{puid} are set, only tracks matching those IDs are returned. The server will ignore C{artistName} and C{releaseTitle} if C{artistId} or ${releaseId} are set respectively. The C{query} parameter may contain a query in U{Lucene syntax <http://lucene.apache.org/java/docs/queryparsersyntax.html>}. Note that C{query} may not be used together with the other parameters except for C{limit} and C{offset}. @param title: a unicode string containing the track's title @param artistName: a unicode string containing the artist's name @param artistId: a string containing the artist's ID @param releaseTitle: a unicode string containing the release's title @param releaseId: a string containing the release's title @param duration: the track's length in milliseconds @param puid: a string containing a PUID @param limit: the maximum number of releases to return @param offset: start results at this zero-based offset @param query: a string containing a query in Lucene syntax """ self._params = [ ('title', title), ('artist', artistName), ('artistid', mbutils.extractUuid(artistId)), ('release', releaseTitle), ('releaseid', mbutils.extractUuid(releaseId)), ('duration', duration), ('puid', puid), ('limit', limit), ('offset', offset), ('query', query), ] if not _paramsValid(self._params): raise ValueError('invalid combination of parameters')
def removeFromUserCollection(self, releases): """Remove releases from a user's collection. The releases parameter must be a list. It can contain either L{Release} objects or a string representing a MusicBrainz release ID (either as absolute URIs or in their 36 character ASCII representation). Removing a release that is not in the collection has no effect. @param releases: a list of releases to remove from the user collection @raise ConnectionError: couldn't connect to server @raise AuthenticationError: invalid user name and/or password """ rels = [] for release in releases: if isinstance(release, Release): rels.append(mbutils.extractUuid(release.id)) else: rels.append(mbutils.extractUuid(release)) encodedStr = urllib.urlencode({'remove': ",".join(rels)}, True) self._ws.post('collection', '', encodedStr)
def __update_result(self, release): """Callback for release detail download from result combo.""" num_results = len(self._resultlist) text = ngettext("Found %d result.", "Found %d results.", num_results) self.result_label.set_text(text % num_results) # issue 973: search can return invalid (or removed) ReleaseIDs if release is None: return self._releasecache.setdefault(extractUuid(release.id), release) self.result_treeview.update_remote_album(release.tracks) self.current_release = release self.release_combo.update(release) save_button = self.get_widget_for_response(Gtk.ResponseType.ACCEPT) save_button.set_sensitive(True)
def _writeRelease(self, xml, release, score=None): if release is None: return types = [mbutils.extractFragment(t) for t in release.getTypes()] typesStr = None if len(types) > 0: typesStr = ' '.join(types) xml.start('release', { 'id': mbutils.extractUuid(release.getId()), 'type': typesStr, 'ext:score': score, }) xml.elem('title', release.getTitle()) xml.elem('text-representation', None, { 'language': release.getTextLanguage(), 'script': release.getTextScript() }) xml.elem('asin', release.getAsin()) self._writeArtist(xml, release.getArtist()) self._writeReleaseGroup(xml, release.getReleaseGroup()) if len(release.getReleaseEvents()) > 0: xml.start('release-event-list') for event in release.getReleaseEvents(): self._writeReleaseEvent(xml, event) xml.end() if len(release.getDiscs()) > 0: xml.start('disc-list') for disc in release.getDiscs(): xml.elem('disc', None, { 'id': disc.getId() }) xml.end() if len(release.getTracks()) > 0: # TODO: count attribute xml.start('track-list', { 'offset': release.getTracksOffset() }) for track in release.getTracks(): self._writeTrack(xml, track) xml.end() self._writeRelationList(xml, release) # TODO: extensions xml.end()
def get_basic_cached_search_result(query_type, query_string): """ Make sure proper CachedSearchResult is present and return its id. Method performs local, then optional remote (MusicBrainz) lookup of query result @param query_type: a string containing query type @param query_string: a string containing query @return: a string containing SHA1 hash of a query string (the ID of a CachedSearchResult document) """ query_id = hashlib.sha1((query_type+query_string).encode('utf-8')).hexdigest() search_result = CachedSearchResult.get_or_create(query_id) search_result.query_string = query_string search_result.query_type = query_type if 'mb' not in search_result.cache_state: #TODO: add 14day window check try: t = mmda_logger('mb','request','search for',query_string) if query_type == 'artist': filter = ws.ArtistFilter(name=query_string,limit=RESULTS_LIMIT) results = mb_query.getArtists(filter) #TODO: add try, or maybe better in 'create_search' as a global wrapper search_result.results = [ {'name':r.artist.name, 'mbid':extractUuid(r.artist.id), 'score':r.score, 'note':r.artist.disambiguation } for r in results ] elif query_type == 'release': filter = ws.ReleaseFilter(title=query_string,limit=RESULTS_LIMIT) results = mb_query.getReleases(filter) #TODO: add try, or maybe better in 'create_search' as a global wrapper search_result.results = [ {'artist':r.release.artist.name, 'title':r.release.title, 'mbid':extractUuid(r.release.id), 'artist_mbid':extractUuid(r.release.artist.id), 'score':r.score, 'tracks_count':r.release.tracksCount, 'year':r.release.getEarliestReleaseEvent().getDate() if r.release.getEarliestReleaseEvent() else None} for r in results ] elif query_type == 'tag': # TODO: refactor to other packages import pylast lastfm = pylast.get_lastfm_network(api_key = settings.LASTFM_API_KEY) lastfm_similar_tags = lastfm.search_for_tag(query_string).get_next_page() search_result.results = [ tag.name for tag in lastfm_similar_tags ] except Exception, e: # TODO: hard error here mmda_logger('search','ERROR',e) raise e else: mmda_logger('mb','result','results',len(search_result.results),t) search_result.cache_state['mb'] = [1,datetime.utcnow()] search_result.save()
def _writeArtist(self, xml, artist, score=None): if artist is None: return xml.start('artist', { 'id': mbutils.extractUuid(artist.getId()), 'type': mbutils.extractFragment(artist.getType()), 'ext:score': score, }) xml.elem('name', artist.getName()) xml.elem('sort-name', artist.getSortName()) xml.elem('disambiguation', artist.getDisambiguation()) xml.elem('life-span', None, { 'begin': artist.getBeginDate(), 'end': artist.getEndDate(), }) if len(artist.getAliases()) > 0: xml.start('alias-list') for alias in artist.getAliases(): xml.elem('alias', alias.getValue(), { 'type': alias.getType(), 'script': alias.getScript(), }) xml.end() if len(artist.getReleases()) > 0: xml.start('release-list') for release in artist.getReleases(): self._writeRelease(xml, release) xml.end() if len(artist.getReleaseGroups()) > 0: xml.start('release-group-list') for releaseGroup in artist.getReleaseGroups(): self._writeReleaseGroup(xml, releaseGroup) xml.end() self._writeRelationList(xml, artist) # TODO: extensions xml.end()
def _writeReleaseGroup(self, xml, rg, score = None): if rg is None: return xml.start('release-group', { 'id': mbutils.extractUuid(rg.getId()), 'type': mbutils.extractFragment(rg.getType()), 'ext:score': score, }) xml.elem('title', rg.getTitle()) self._writeArtist(xml, rg.getArtist()) if len(rg.getReleases()) > 0: xml.start('release-list') for rel in rg.getReleases(): self._writeRelease(xml, rel) xml.end() xml.end()
def findArtist(self, name): page = [templates._header] if len(name) == 0 or name == 'Add an artist': raise cherrypy.HTTPRedirect("/") else: artistResults = ws.Query().getArtists(ws.ArtistFilter(string.replace(name, '&', '%38'), limit=8)) if len(artistResults) == 0: page.append('''No results!<a class="blue" href="/">Go back</a>''') elif len(artistResults) > 1: page.append('''Search returned multiple artists. Click the artist you want to add:<br /><br />''') for result in artistResults: artist = result.artist page.append('''<a href="/addArtist?artistid=%s">%s</a> (<a class="externalred" href="/artistInfo?artistid=%s">more info</a>)<br />''' % (u.extractUuid(artist.id), artist.name, u.extractUuid(artist.id))) return page else: for result in artistResults: artist = result.artist raise cherrypy.HTTPRedirect("/addArtist?artistid=%s" % u.extractUuid(artist.id))
def getLabelById(self, id_, include=None): """Returns a L{model.Label} If no label with that ID can be found, or there is a server problem, an exception is raised. @param id_: a string containing the label's ID. @raise ConnectionError: couldn't connect to server @raise RequestError: invalid ID or include tags @raise ResourceNotFoundError: release doesn't exist @raise ResponseError: server returned invalid data """ uuid = mbutils.extractUuid(id_, 'label') result = self._getFromWebService('label', uuid, include) label = result.getLabel() if label is not None: return label else: raise ResponseError("server didn't return a label")
def get_basic_release(mbid): """ Make sure release and its dependencies are present and contain required data. @param mbid: a string containing a MusicBrainz ID of an artist @return: a CachedReleaseGroup object containing required minimal data set """ release_group = CachedReleaseGroup.view('artists/releases',include_docs=True, key=mbid).one() if not release_group: # TODO: optimize? its just one additional request on rare ocassions tho.. try: t = mmda_logger('mb','request','artist mbid of release',mbid) mb_release = mb_query.getReleaseById(mbid, MB_RELEASE_ARTIST) artist_mbid = extractUuid(mb_release.artist.id) mmda_logger('mb','result','artist mbid',artist_mbid,t) except WebServiceError, e: # TODO: add error handling here mmda_logger('mb-release','ERROR',e) raise e else: get_basic_artist(artist_mbid) release_group = CachedReleaseGroup.view('artists/releases',include_docs=True, key=mbid).one()
def getArtistById(self, id_, include=None): """Returns an artist. If no artist with that ID can be found, C{include} contains invalid tags or there's a server problem, an exception is raised. @param id_: a string containing the artist's ID @param include: an L{ArtistIncludes} object, or None @return: an L{Artist <musicbrainz2.model.Artist>} object, or None @raise ConnectionError: couldn't connect to server @raise RequestError: invalid ID or include tags @raise ResourceNotFoundError: artist doesn't exist @raise ResponseError: server returned invalid data """ uuid = mbutils.extractUuid(id_, 'artist') result = self._getFromWebService('artist', uuid, include) artist = result.getArtist() if artist is not None: return artist else: raise ResponseError("server didn't return artist")
def getTrackById(self, id_, include=None): """Returns a track. If no track with that ID can be found, C{include} contains invalid tags or there's a server problem, an exception is raised. @param id_: a string containing the track's ID @param include: a L{TrackIncludes} object, or None @return: a L{Track <musicbrainz2.model.Track>} object, or None @raise ConnectionError: couldn't connect to server @raise RequestError: invalid ID or include tags @raise ResourceNotFoundError: track doesn't exist @raise ResponseError: server returned invalid data """ uuid = mbutils.extractUuid(id_, 'track') result = self._getFromWebService('track', uuid, include) track = result.getTrack() if track is not None: return track else: raise ResponseError("server didn't return track")
if tr: tp.releaseTrack(tr) if status == tunepimp.eError: print ("%s:\n\tError: %s\n" % (fileName, tp.getError())).encode('ascii', 'ignore') tp.remove(fileId) if addPUID: try: fileName, puid, trackId = addPUID print ("%s:\n\t%s - %s" % (fileName, puid, trackId)).encode('ascii', 'ignore') flt = TrackFilter(puid=puid) result = q.getTracks(flt) found = False for res in result: if extractUuid(res.track.id, 'track') == trackId: found = True if not found: toSubmit[trackId] = puid print else: print "\tAlready in MB, skipping.\n" except: print "EXCEPTION THROWN LOOKING UP TRACK ID" tp.remove(fileId) if len(toSubmit) >= batchSize or (tp.getNumFiles() == 0 and len(toSubmit) > 0): print "Submitting %d PUIDs to MusicBrainz...\n" % (len(toSubmit),) q.submitPuids(toSubmit) toSubmit = {}
def createDiscMetadata(release, cdid, numTracks, toc): discMeta = DiscMetadata() album = release.title albid = extractUuid(release.id, 'release') rel_artId = artId = extractUuid(release.artist.id, 'artist') rel_artist = artist = release.artist.name rel_artistSort = artistSort = release.artist.sortName discMeta.variousArtists = (release.artist.id == model.VARIOUS_ARTISTS_ID) releaseYear = None for event in release.releaseEvents: releaseDate = event.date releaseCountry = event.country thisReleaseYear = int(releaseDate[0:4]) if releaseYear == None or thisReleaseYear < releaseYear: releaseYear = thisReleaseYear discNumMatches = DISC_NUM_REGEX.findall(album) if discNumMatches: discNum = int(discNumMatches[0][0]) else: discNum = 1 discMeta.title = album discMeta.artist = artist discMeta.artistSort = artistSort discMeta.mbDiscId = cdid discMeta.toc = toc discMeta.mbAlbumId = albid discMeta.mbArtistId = artId discMeta.discNumber = (discNum, discNum) discMeta.releaseDate = releaseYear lastArtist = None logging.info("\t%s / %s" % (artist, album)) trackNum = 0 for track in release.tracks: trackNum += 1 name = track.title artist = track.artist.name if track.artist else rel_artist artistSort = track.artist.sortName if track.artist else rel_artistSort artId = extractUuid(track.artist.id, 'artist') if track.artist else rel_artId dura = track.duration if track.duration else 0 trackURI = track.id trackId = extractUuid(trackURI, 'track') trackMeta = TrackMetadata() trackMeta.title = name trackMeta.artist = artist if lastArtist and artist <> lastArtist: discMeta.variousArtists = True lastArtist = artist trackMeta.artistSort = artistSort trackMeta.number = trackNum trackMeta.length = dura trackMeta.mbTrackId = trackId trackMeta.mbArtistId = artId discMeta.tracks.append(trackMeta) dura = "%d:%02d" % divmod(int(dura / 1000), 60) logging.info("\t%02d - %s - %s (%s)" % (trackNum, artist, name, dura)) return discMeta
def __save(self, widget=None, response=None): """Writes values to Song objects.""" self._qthread.stop() if response != Gtk.ResponseType.ACCEPT: self.destroy() return album = self.current_release shared = {} shared['album'] = album.title if config_get('split_disc', True): m = re.match(r'(.*) \(disc (.*?)\)$', album.title) if m: shared['album'] = m.group(1) disc = m.group(2).split(': ', 1) shared['discnumber'] = disc[0] if len(disc) > 1: shared['discsubtitle'] = disc[1] relevt = self.release_combo.get_release_event() shared['date'] = relevt and relevt.getDate() or '' if shared['date'] and config_get('year_only', False): shared['date'] = shared['date'].split('-')[0] if config_get('labelid', True): if relevt and relevt.getCatalogNumber(): shared['labelid'] = relevt.getCatalogNumber() if not album.isSingleArtistRelease(): if (config_get('albumartist', True) and extractUuid(album.artist.id) != VARIOUS_ARTISTS_ARTISTID): shared['albumartist'] = album.artist.name if config_get('artist_sort', False) and \ album.artist.sortName != album.artist.name: shared['albumartistsort'] = album.artist.sortName if config_get('standard', True): shared['musicbrainz_albumartistid'] = extractUuid(album.artist.id) shared['musicbrainz_albumid'] = extractUuid(album.id) for idx, (song, ) in enumerate(self.result_treeview.model): if song is None: continue song.update(shared) if idx >= len(album.tracks): continue track = album.tracks[idx] song['title'] = track.title song['tracknumber'] = '%d/%d' % ( idx + 1, max(len(album.tracks), len( self.result_treeview.model))) if config_get('standard', True): song['musicbrainz_trackid'] = extractUuid(track.id) if album.isSingleArtistRelease() or not track.artist: song['artist'] = album.artist.name if config_get('artist_sort', False) and \ album.artist.sortName != album.artist.name: song['artistsort'] = album.artist.sortName else: song['artist'] = track.artist.name if config_get('artist_sort', False) and \ track.artist.sortName != track.artist.name: song['artistsort'] = track.artist.sortName if config_get('standard', True): song['musicbrainz_artistid'] = extractUuid(track.artist.id) if config_get('split_feat', False): feats = re.findall(r' \(feat\. (.*?)\)', track.title) if feats: feat = [] for value in feats: values = value.split(', ') if len(values) > 1: values += values.pop().split(' & ') feat += values song['performer'] = '\n'.join(feat) song['title'] = re.sub(r' \(feat\. .*?\)', '', track.title) self.destroy()