def get_search_results(old_album, old_title, old_artist, old_date, old_track_number): results = musicbrainzngs.search_recordings(artist = old_artist, recording = old_title, release = old_album, tnum = old_track_number, limit = 5) """this sequence of if statements is designed to query the database using incomplete parameters. This is useful in the event that some of the pre-existing tag information is incorrect""" if not results['recording-list']: results = musicbrainzngs.search_recordings(recording = old_title, release = old_album, limit = 5) if not results['recording-list']: results = musicbrainzngs.search_recordings(artist = old_artist, recording = old_title, limit = 5) if not results['recording-list']: #TODO: Search by duration results = musicbrainzngs.search_recordings(recording = old_title, limit = 5) return results['recording-list']
def crawl_one_file(self, name_file, directory): if is_extension_managed(name_file): audio = get_file_manager(name_file, directory) tags = audio.get_tag_research() if tags[0] == '' and tags[1] == '': # Either filename if no_tags mz_query = remove_extension(name_file) self.tag_finder[name_file] = reorder_data( mb.search_recordings(query=mz_query, limit=1)) self.tree_view.add_crawled([name_file]) else: # Using tags title artist and album if they are present if tags[0] != '' and tags[1] != 0: try: gathered_data = mb.search_recordings( recording=tags[0], artistname=tags[1], limit=1) self.tag_finder[name_file] = reorder_data( gathered_data) self.tree_view.add_crawled([name_file]) except mb.NetworkError: pass elif tags[1] == '': try: records = mb.search_recordings(recording=tags[0], release=tags[2], limit=1) records = reorder_data(records) self.tag_finder[name_file] = records self.tree_view.add_crawled([name_file]) except mb.NetworkError: pass elif tags[0] == '': try: gathered_data = mb.search_recordings( query=remove_extension(name_file), artistname=tags[1], limit=1) reordered_data = reorder_data(gathered_data) self.tag_finder[name_file] = reordered_data self.tree_view.add_crawled([name_file]) except mb.NetworkError: self.tree_view.add_crawled([name_file]) else: self.tree_view.add_crawled([name_file])
def search_mb(): musicbrainzngs.set_useragent('music-downloader', '0.1') criteria = {} if len(query_comps) > 1: criteria = {'artist': clear_comps[0], 'recording': clear_comps[1]} try: if criteria: recordings = musicbrainzngs.search_recordings(limit=10, **criteria) else: recordings = musicbrainzngs.search_recordings(limit=10, query=clear_query) except Exception as e: print "Failure: Something went wrong" sys.exit() for recording in recordings['recording-list']: yield get_mb_info(recording)
def search(musicfile: MusicFile): try: filename = fsplitter.get_file_name(musicfile.file_path) check_filename_not_useful(filename) file_name_fragments = fsplitter.split(filename) recording = None iterations = 0 for fragment in file_name_fragments: if iterations > 5: logging.warning("Crossed 5 iterations...moving on") break iterations += 1 try: result = musicbrainzngs.search_recordings(fragment) found_valid_match, recording = validate_result( result, fragment, file_name_fragments) if found_valid_match: update_musicfile(musicfile, recording) return musicfile except musicbrainzngs.WebServiceError as web_service_error: logging.warning( f"Something went wrong with the request: {web_service_error}" ) return web_service_error except FilenameNotUseful as filename_not_useful: logging.warning( f"FilenameNotUseful exception encountered. No need to continue search.\n Exception: {filename_not_useful}" )
def search_rec_with_artist(queryRecName, artist, mbSet): queryRecNameQuotes = '"%s"' % queryRecName artistMBID = artist['id'] offset = 0 limit = 100 while True: result = mb.search_recordings(queryRecNameQuotes, arid=artistMBID, offset=offset, limit=limit) recs = result['recording-list'] for r in recs: if not match( r['title'], queryRecName) : break # match recordingMBIDs in ABrainz if isRecordingInAbrainz(r, mbSet ): print "found recording with title {} with artist {} and with rec MBID {} in acoustic brainz".format(r["title"].encode('utf-8','replace'), artist["name"].encode('utf-8','replace'), r['id'].encode('utf-8','replace') ) return r if offset > 200: print "Looked for this song in the 1000 first results but didn't find it, stopping" break offset += limit return None
def panTag(song, path): tag = MP4(path) dur = str(int(tag.info.length * 1000)) res = _brain.search_recordings(limit=1, query=song.title, artist=song.artist, release=song.album, qdur=dur)['recording-list'][0] sco = res['ext:score'] if sco == '100': tag['----:com.apple.iTunes:MusicBrainz Track Id'] = res['id'] tag['\xa9ART'] = song.artist tag['\xa9alb'] = song.album tag['\xa9nam'] = song.title tag.save() xbmc.log( "%s.Tag OK (%13s,%4s %%) '%s - %s - %s'" % (_plugin, _stamp, sco, song.songId[:4], song.artist, song.title)) return True else: xbmc.log( "%s.Tag FAIL (%13s,%4s %%) '%s - %s - %s'" % (_plugin, _stamp, sco, song.songId[:4], song.artist, song.title)) return False
def get_metadata(parsed): temp = [] results = [] search_results = musicbrainzngs.search_recordings( recording=parsed['title'], artist=parsed['artist'], limit=50) for recording in search_results['recording-list']: if re.sub(r' |\.|\\|/|\|', '', parsed['title']).lower() in re.sub( r' |\.|\\|/|\|', '', recording['title']).lower() or re.sub( r' |\.|\\|/|\|', '', recording['title']).lower() in re.sub( r' |\.|\\|/|\|', '', parsed['title']).lower(): if 'release-list' in recording: for release in recording['release-list']: artist = recording['artist-credit'][0]['artist']['name'] title = recording['title'] album = release['title'] id = release['id'] entry = { 'artist': artist, 'title': title, 'album': album, } if entry not in temp and valid(recording, release, entry, id): temp.append(entry.copy()) entry['img'] = get_cover_art_as_data(id) entry['url'] = parsed['url'] results.append(entry) return results
def get_tracks(self, id): time.sleep(random.uniform(0.1, 1.0)) try: dataArray = [] trackresult = musicbrainzngs.search_recordings(reid=id) for recording in trackresult['recording-list']: payload = self.create_payload(['musicbrainz.track']) payload['mbtrack']['title'] = recording['title'] # print(recording) if 'length' in recording: duration = float(recording['length']) # print(duration) durationInSeconds = int(duration/1000) timeObj = self.durationToHHMMSS(durationInSeconds) timeString = timeObj['hour'] + ":" + timeObj['minute'] + ":" + timeObj['second'] payload['mbtrack']['duration'] = { 'seconds': durationInSeconds, 'formatted': timeString, 'original': { 'seconds': durationInSeconds } } dataArray.append(payload) return dataArray except musicbrainzngs.NetworkError: self.printMessage("Could not connect to musicbrainz server at:" + MBHOST) return None
def get_recording_date(artist, album, title): t = "artist = {}; album = {} [not used in search], title = {} [in get_recording_date]".format(artist, album, title) print t.encode('ascii', 'ignore') try: result = musicbrainzngs.search_recordings(artist=artist, recording=title, limit=40, offset=None, strict=False) except: return "No date exception (search_recordings)" recording_list = result.get('recording-list') if recording_list is None: return "No date (search of musicbrainzngs did not produce a recording_list)" dates = [] for d in recording_list: if int(d['ext:score']) > 98 and 'release-list' in d: rel_dict = d['release-list'][0] # it's a list but seems to have one element and that's a dictionary date = rel_dict.get('date', '9999')[0:4] title = rel_dict.get('title','No title') if rel_dict.get('artist-credit-phrase') == 'Various Artists': #possibly could also use status:promotion dates.append((date,title,'z')) else: dates.append((date,title,'a')) if dates: dates.sort(key=itemgetter(0,2)) # idea is to put albums by the artist ahead of albums by various artists return u"{} - {}".format(dates[0][0], dates[0][1]) else: return ''
async def toptrack(self, ctx, username): payload = { "method": "user.gettoptracks", "user": username, "period": "overall", "limit": "1" } trackdata = self.api(payload) response = discord.Embed(title="toptrack") if "error" in trackdata: response.add_field(name="Failed to load top track", value=trackdata["message"]) await ctx.send(embed=response) return toptrack = trackdata["toptracks"]["track"][0] response.add_field(name="{0}'s top track".format(username), value="{0} - {1}".format(toptrack["artist"]["name"], toptrack["name"])) recording = musicbrainzngs.search_recordings(tid=toptrack["mbid"]) recording = recording["recording-list"][0] release = recording["release-list"][0] imgs = musicbrainzngs.get_image_list(release["id"]) url = requests.get(imgs["images"][0]["thumbnails"]["small"]) url = url.url response.set_thumbnail(url=url) await ctx.send(embed=response)
def validate_artist_song(artist_name, song_name): ''' Makes sure we have the song by the artist in the DB, otherwise it downloads the relevant information about it and stores it in the DB ''' artist_id = validate_artist(artist_name) song_id = song_in_db(song_name, artist_id) if song_id: return artist_id, song_id logger.debug("Getting MusicBrainz info for %s by %s", song_name, artist_name) response = try_mb_request(lambda: mb.search_recordings(artist=artist_name, release=song_name)) if 0 != response['recording-count']: recording_json = response['recording-list'][0] # Currentyl we take the first, maybe we should check? release_date = None if 'release-list' in recording_json: for release in recording_json['release-list']: if 'date' in release: release_date = parse(release['date']) break else: logger.warning('Got 0 possible releases for "%s" by "%s", skipping', song_name, artist_name) release_date = None song_id = insert_song(song_name=song_name, artist_id=artist_id, release_date=release_date) song_lyrics = get_song_lyrics(song_name, artist_name) if song_lyrics: insert_lyrics(song_id, song_lyrics) return artist_id, song_id
def assign_songs(self, cleaned_tweets): print "Matching songs to tweets" cleaner_tweets = [] tweets_used = 0 for cleaned_tweet in cleaned_tweets: if " by " in cleaned_tweet['text']: split_text = cleaned_tweet['text'].split(' by ') artists_list = mbz.search_artists(split_text[0]) try: mbID = artists_list['artist-list'][0]['id'] works = mbz.search_recordings(query=split_text[0], arid=mbID) tweet = dict() tweet['song'] = works['recording-list'][0]['title'] tweet['text'] = cleaned_tweet['text'] tweet['artist'] = artists_list['artist-list'][0]['name'] tweet['musicbrainzID'] = works['recording-list'][0]['id'] tweet['userID'] = cleaned_tweet['userID'] cleaner_tweets.append(tweet) tweets_used += 1 except (UnicodeEncodeError, IndexError): print "Unable to find song/artist" print "Tweets used: %s" % (tweets_used) return cleaner_tweets
def music_chart_tracks(locale: str): recordings = musicbrainzngs.search_recordings("*", limit=100) doc: Document = minidom.Document() feed: Element = create_feed(doc, "tracks", "tracks", f"/v3.2/{locale}/music/chart/zune/tracks") for recording in recordings["recording-list"]: # Set track ID and Title id: str = recording["id"] title: str = recording["title"] entry: Element = create_entry( doc, title, id, f"/v3.2/{locale}/music/collection/features/" + id) # Get artist ID and Name artist = recording["artist-credit"][0]["artist"] artist_id: str = artist["id"] artist_name: str = artist["name"] # Create primaryArtist element primary_artist_elem: Element = doc.createElement("primaryArtist") artist_id_element: Element = doc.createElement("id") set_element_value(artist_id_element, artist_id) primary_artist_elem.appendChild(artist_id_element) artist_name_element: Element = doc.createElement("name") set_element_value(artist_name_element, artist_name) primary_artist_elem.appendChild(artist_name_element) entry.appendChild(primary_artist_elem) feed.appendChild(entry) xml_str = doc.toprettyxml(indent="\t") return Response(xml_str, mimetype=MIME_XML)
def load_candidates(search_title: str, search_artist: str, candidate_limit: int) -> List[MbRecording]: data = musicbrainzngs.search_recordings(recording=search_title, artist=search_artist, limit=candidate_limit) candidates: List[MbRecording] = data['recording-list'] return candidates
def _get_metadata(self, parsed): results = [] temp = [] artists = parsed['artists'] if 'artists' in parsed else None artist = artists[0] if artists else '' artistname = artists[1] if artists and len(artists) > 1 else '' mb_results = musicbrainzngs.search_recordings(query=parsed['title'], artist=artist, artistname=artistname, limit=20) for recording in mb_results['recording-list']: if 'release-list' in recording: title = recording['title'] if ('artists' not in parsed or re.sub(r'\W', '', title.lower()) == re.sub(r'\W', '', parsed[ 'title'].lower())) and self._valid_title(title): artists = [a['artist']['name'] for a in recording['artist-credit'] if isinstance(a, dict) and 'artist' in a] # Only use the first artist (may change in the future) artist = artists[0] for release in recording['release-list']: album = release['title'] album_id = release['id'] entry = { 'url': parsed['url'], 'title': title, 'artist': artist, 'album': album } if entry not in temp and self._valid(release): temp.append(entry.copy()) entry['id'] = album_id entry['img'] = self._cover_art_cache[ album_id] if album_id in self._cover_art_cache else self._get_cover_art(album_id) results.append(entry) return results
def search_musicbrainz_by_recording(data): musicbrainz.set_useragent("mer-ml", "0.1") for i, row in data.iterrows(): query = row['title'] + ' ' + row['artist'] print('Query: ') print(query) recordings = musicbrainz.search_recordings(query=query) if not recordings['recording-list']: print("recording not found") else: # find closest artist and title matches query_matches = find_closest_recording(recordings, query) if query_matches: for recording in recordings['recording-list']: if format_string(recording['title'] + ' ' + recording['artist-credit-phrase'] ) in query_matches: print('Found: ') print(recording) if has_audio_data(recording['id']): data.at[i, 'id'] = recording['id'] # keep first match break else: data.at[i, 'fallback-id'] = recording['id'] print('no audio data') return data
def handle_data( dict ) : if verbose: print (u"{} in {} by {}".format(dict['song'], dict['album'], dict['artist'] ) ) result = m.search_recordings(artist = dict['artist'], recording=dict['song'], country='US', limit=15) lowestDate = 9999 bestAlbum = dict['album'] bestArtist = dict['artist'] for (idx, release) in enumerate(result['recording-list']): (newsong, newartist, newalbum, newdate) = getinfo( release, dict['song'], dict['album'], dict['artist'] ) if (lowestDate > newdate): lowestDate = newdate bestAlbum = newalbum bestArtist = newartist if verbose: print(u" {} in {} by {} on {}".format( newsong, newalbum, newartist, newdate) ) if verbose: print() if verbose: print(u"----{} in {} by {} on {}".format( dict['song'], bestAlbum, bestArtist, lowestDate) ) return (dict['song'], bestAlbum, bestArtist, lowestDate)
def search_song_by_title(song_query, limit=20): try: # Oversearch, prune later to remove duplicates query_results = mbn.search_recordings(query=song_query, limit=2 * limit) result_list = query_results['recording-list'] song_list = [] song_string_list = [] for result in result_list: title = result['title'] artist = result['artist-credit-phrase'] mbid = result['id'] #album = result['release-list'][0]['title'] song = Song( title=title, artist=artist, mbid=mbid, #album=album) album='None') song_string = str.lower(str(song)) if song_string not in song_string_list: song_string_list.append(song_string) song.save() song_list.append(song) return song_list[:limit] except (mbn.ResponseError): return []
def fetch_recording(artist, title, **kwarg): """fetch recording from musicBrainz """ result = musicbrainzngs.search_recordings(query='', limit=10, offset=None, strict=False, artist = artist, release = title) seq2 =' '.join([title, artist]) high_score = 0 idx = 0 for i in range(0,10): seq1 = ' '.join([result['recording-list'][i]['title'], result['recording-list'][i]['artist-credit-phrase']]) similarity_score = similar(seq1,seq2) if similarity_score > high_score and 'instrumental' not in seq1 and (not 'disambiguation' in result['recording-list'][i] or \ (result['recording-list'][i]['disambiguation'] != 'music video')): high_score = similarity_score idx = i '''print similarity_score print seq1 print seq2''' #print idx recording = {} recording['match_score'] = high_score if 'length' in result['recording-list'][idx]: recording['recording_length'] = result['recording-list'][idx]['length'] else: recording['recording_length'] = -1 recording['recording_mbid'] = result['recording-list'][idx]['id'] recording['artist_mbid'] = result['recording-list'][idx]['artist-credit'][0]['artist']['id'] recording['release_date'] = result['recording-list'][idx]['release-list'][0]['date'] recording['release_mbid'] = result['recording-list'][idx]['release-list'][0]['id'] if 'artist-credit-phrase' in result['recording-list'][idx]: recording['mb_artist'] = result['recording-list'][idx]['artist-credit-phrase'] else: recording['mb_artist'] = '' recording['mb_title'] = result['recording-list'][idx]['title'] #print result['recording-list'] return recording
def lookup_song(artist, title, format_string, sql): musicbrainzngs.set_useragent('Song-Looker-Upper', '0.1', 'http://www.kbarnes3.com') query = '"{0}" AND artist:"{1}" AND status:official'.format(title, artist) result = musicbrainzngs.search_recordings(query) if len(result['recording-list']) < 1: print('No songs found for query ' + query, file=sys.stderr) return recording = result['recording-list'][0] try: artist = recording['artist-credit'][0]['artist']['name'] artist = artist.replace(u'\u2019', "'") title = recording['title'] title = title.replace(u'\u2019', "'") if sql: artist = artist.replace("'", "''") title = title.replace("'", "''") length = recording['release-list'][0]['medium-list'][1]['track-list'][0]['length'] duration = int(int(length) / 1000) output = format_string.format(artist=artist, title=title, duration=duration) print(output, end='') except Exception as ex: print('Error handling recording: ' + str(recording), file=sys.stderr) print(ex.message, file=sys.stderr) raise RecordParseException(ex.message)
def handle_data(dict): if verbose: print(u"{} in {} by {}".format(dict['song'], dict['album'], dict['artist'])) result = m.search_recordings(artist=dict['artist'], recording=dict['song'], country='US', limit=15) lowestDate = 9999 bestAlbum = dict['album'] bestArtist = dict['artist'] for (idx, release) in enumerate(result['recording-list']): (newsong, newartist, newalbum, newdate) = getinfo(release, dict['song'], dict['album'], dict['artist']) if (lowestDate > newdate): lowestDate = newdate bestAlbum = newalbum bestArtist = newartist if verbose: print(u" {} in {} by {} on {}".format(newsong, newalbum, newartist, newdate)) if verbose: print() if verbose: print(u"----{} in {} by {} on {}".format(dict['song'], bestAlbum, bestArtist, lowestDate)) return (dict['song'], bestAlbum, bestArtist, lowestDate)
def get_song_names(artist_dict): ''' This function takes an artist dictioanry as supplied by the MusicBrainz API :param artist_dict: dictionary as retreived from musicbrainz api :return: dictionary of song names from artist ''' artist_dict = {} # Do STUFF with the song names artists = ['Caro Emerald'] song_names = {} #Only test code can be commented out when we use artist_songs.py for a in artists: artist_dict[a] = musicbrainzngs.search_recordings(artist=a, limit=25) #For all artists get the song names for a in artists: print(artist_dict) for recording in artist_dict[a]['recording-list']: song_name = recording['title'] print(song_name) if a in song_names: song_names[a].append(song_name) else: song_names[a] = [song_name] print(song_names) print("###################") return song_names
def updateMetaDataInteractive(self): print('Last try. Is the file in this list.') #we look up the song and download the dictionary of recording stats musicbrainzngs.set_useragent('CS3030MusicApp', 'V0.5') if (self.artist != '' or self.title != ''): temp = '{0} AND artist:{1}'.format(self.title.replace(' ', '_'), self.artist) results = musicbrainzngs.search_recordings(query=temp, limit=25) lineNum = 1 releaseList = [] for release in results['recording-list']: try: print(str(lineNum) + '\tTitle: ' + release['title'], 'Artist: ' + release['artist-credit'][0]['artist']['name'], 'ID: ' + release['id'], 'Album: ' + release['release-list'][0]['title']) lineNum += 1 releaseList.append(release) except Exception as e: pass userInput = input("Which song matches your file? ") try: userInput = int(userInput) result = releaseList[int(userInput) - 1] self.setMetaData(result['title'], result['artist-credit-phrase'], result['release-list'][0]['title']) except Exception as e: print('Choice not found.')
def _find_recordings(artist_name, track_name): query = QueryBuilderFactory.create("recording") Fields = query.get_fields_enum() query.put(Fields.NAME, track_name).AND() \ .put(Fields.ARTIST, artist_name).AND() \ .NOT().put(Fields.SECONDARY_TYPE, "Live").AND() \ .put(Fields.STATUS, "Official") return musicbrainzngs.search_recordings(query=query.build())["recording-list"]
def get_release_date(artist, album, title): try: #print "artist = {}; album = {} [not used in search], title = {} [in get_release_date]".format(artist, album, title) t = "artist = {}; album = {} [not used in search], title = {} [in get_release_date]".format(artist, album, title) print t.encode('ascii', 'ignore') except UnicodeEncodeError as e: # should just be .encode('ascii', 'ignore') print "Unicode Error", e ## commented this out because I think in most circumstances where there is a legit album, there is an accompanying date ## (like for a ripped CD, a Rhapsody song, Pandora ## In addition, this allows you to display the first album where the song appeared # try: # result = musicbrainzngs.search_releases(artist=artist, release=album, limit=20, strict=True) # except: # return "No date exception (search_releases)" # #release_list = result['release-list'] # can be missing # if 'release-list' in result: # release_list = result['release-list'] # can be missing # dates = [d['date'][0:4] for d in release_list if 'date' in d and int(d['ext:score']) > 90] # if dates: # dates.sort() # return dates[0] ### Generally if there was no date provided it's because there is also a bogus album (because it's a collection ### and so decided to comment out the above. We'll see how that works over time. try: result = musicbrainzngs.search_recordings(artist=artist, recording=title, limit=40, offset=None, strict=False) except: return "No date exception (search_recordings)" recording_list = result.get('recording-list') if recording_list is None: return "No date (search of musicbrainzngs did not produce a recording_list)" dates = [] for d in recording_list: if int(d['ext:score']) > 98 and 'release-list' in d: rel_dict = d['release-list'][0] # it's a list but seems to have one element and that's a dictionary date = rel_dict.get('date', '9999')[0:4] title = rel_dict.get('title','No title') if rel_dict.get('artist-credit-phrase') == 'Various Artists': #possibly could also use status:promotion dates.append((date,title,'z')) else: dates.append((date,title,'a')) if dates: dates.sort(key=itemgetter(0,2)) # idea is to put albums by the artist ahead of albums by various artists return u"{} - {}".format(dates[0][0], dates[0][1]) else: return "?"
def songDataTest(songName): songData = musicData.search_recordings(songName, limit=5) for songData in songData['recording-list']: if (songData == []): print("empty") else: print("title: " + songData['title'] + " | artist: " + songData['artist-credit-phrase'])
def searchItems( resourcetype, parameters ): luceneQuery = "" title = parameters['title'] if title is not None and title != "": luceneQuery = 'recording:"' + title + '" ' track = parameters['track'] if track is not None and track != "": luceneQuery = 'tnum:' + track + ' ' artist = parameters['author'] if artist is not None and artist != "": luceneQuery = 'artistname:"' + artist + '" ' album = parameters['album'] if album is not None and album != "": luceneQuery = 'release:"' + album + '" ' searchResults = [] try: result = musicbrainzngs.search_recordings(luceneQuery, strict=True) if not result['recording-list']: WebExtractor.log( 'No results found for: ' + luceneQuery ) # iterate over all available results for recording in result['recording-list']: if int(recording['ext:score']) < 50: continue detailString ="" albumRelese = recording['release-list'][0] if albumRelese is not None: detailString = albumRelese['title'] if 'date' in albumRelese: detailString = detailString + ' (' + albumRelese['date'] + ')' if 'type' in albumRelese['release-group']: detailString = detailString + ' - ' + albumRelese['release-group']['type'] entryDict = dict ( title = recording["artist-credit-phrase"] + ' - ' + recording["title"], details = detailString, url = 'http://musicbrainz.org/recording/' + recording['id'], distanceString = recording["artist-credit-phrase"] + recording["title"] ) searchResults.append(entryDict) WebExtractor.searchResults( searchResults ) except Exception as err: WebExtractor.error("Script error: \n" + str(err))
def musicbrainz(query): m.set_useragent('application', '0.01', 'http://example.com') rec = m.search_recordings(query=query, limit=1)['recording-list'][0] release_id = rec['release-list'][0]['id'] return dict(artist_name=rec['artist-credit-phrase'], length=rec['length'], title=rec['title'], release=release_id)
def search_mb(self): musicbrainzngs.set_useragent("mudl", "0.1.3") criteria = {"artist": self.user_query.bare_artist(), "recording": self.user_query.bare_title()} try: recordings = musicbrainzngs.search_recordings(limit=10, **criteria) for recording in recordings["recording-list"]: yield VKDownloader.get_mb_info(recording) except Exception as e: print(colored.red(u"Something went wrong with the request: {0}".format(e)))
def get_artist_info(self, artists): ''' This function uses the musicbrainz API and takes a list of artist names and finds the songs they made / sung etc. :param artists: list of artist names :return: dictionary with artist name as a key and a dictionary of songs with metadata about the song ''' artist_dict = {} works_dict = {} # Get the first results and put in dictionary (required to get teh recording count for a in artists: artist_dict[a] = musicbrainzngs.search_recordings(artist=a, limit=25) for k in artist_dict: for rec in artist_dict[k]['recording-list']: # works_dict[k] = musicbrainzngs.get_work_by_id(id=rec['id']) print(rec) print(works_dict) # Now find all the songs for each artist based on the offset gained from the recording count variable for a in artists: #recording_count = artist_dict[a]['recording-count'] #For testing recording_count = 25 offs = 25 # While songs are left keep appending them to the recording-list while recording_count > 0: for i in range(divmod(recording_count, 25)[0] + 1): print(i) fetch = musicbrainzngs.search_recordings(artist=a, recording='22', limit=25, offset=offs) artist_dict[a]['recording-list'] += fetch['recording-list'] offs += 25 recording_count -= 25 # For testing purposes only #f = open('caro_dict.txt', 'w') # f.write(str(artist_dict)) # f.close() # print(artist_dict) return artist_dict
def _request(query, artist, album=None, track=None): results = None if track: results = musicbrainzngs.search_recordings(query=query) elif album: results = musicbrainzngs.search_releases(query=query) elif artist: results = musicbrainzngs.search_artists(query=query) return results
def get_recording_by_isrc(self, isrc, album): try: results = mbz.get_recordings_by_isrc(isrc) except: abort(500, message='CANNOT FIND {} in MusicBrainz'.format(album)) recordings = [] for each in results['isrc']['recording-list']: entry = mbz.search_recordings(rid=each['id']) recordings.append(entry) return recordings
def mbz_query(self, artist, recording): query_dict = {'recordings': None, 'artists': None} if recording: query_dict['recordings'] = mbz.search_recordings(recording, artist) return query_dict elif artist: query_dict['artists'] = mbz.search_artists(artist) return query_dict else: return None
def musicbrainz_ids_by_isrc(): res = musicbrainzngs.search_recordings(query='') res = musicbrainzngs.browse_events( area=None, artist='650e7db6-b795-4eb5-a702-5ea2fc46c848', place=None, includes=[], limit=100, offset=None) pp.pprint(res)
def music_track(locale: str): query: str = request.args.get("q") response = musicbrainzngs.search_recordings(query) doc: Document = minidom.Document() feed: Element = create_feed(doc, "tracks", "tracks", request.endpoint) for recording in response["recording-list"]: try: # Set track ID and Title id: str = recording["id"] title: str = recording["title"] entry: Element = create_entry(doc, title, id, f"/v3.2/{locale}/music/track/" + id) # Get artist ID and Name artist = recording["artist-credit"][0]["artist"] artist_id: str = artist["id"] artist_name: str = artist["name"] # Create primaryArtist element primary_artist_elem: Element = doc.createElement("primaryArtist") artist_id_element: Element = doc.createElement("id") set_element_value(artist_id_element, artist_id) primary_artist_elem.appendChild(artist_id_element) artist_name_element: Element = doc.createElement("name") set_element_value(artist_name_element, artist_name) primary_artist_elem.appendChild(artist_name_element) entry.appendChild(primary_artist_elem) # Get album ID and Title album = recording["release-list"][0] album_id: str = album["id"] album_name: str = album["title"] # Create album element album_elem: Element = doc.createElement("album") album_id_element: Element = doc.createElement("id") set_element_value(album_id_element, album_id) album_elem.appendChild(album_id_element) album_name_element: Element = doc.createElement("title") set_element_value(album_name_element, album_name) album_elem.appendChild(album_name_element) entry.appendChild(album_elem) feed.appendChild(entry) except: pass xml_str = doc.toprettyxml(indent="\t") return Response(xml_str, mimetype=MIME_XML)
def lookup_tracks(input, output=None, verbose=False): mb.set_useragent('xpn-a2z', '0.1', 'https://github.com/asudell/a2z') with open(input) as songs: reader = csv.DictReader(songs) for row in reader: if verbose: print "looking up %s by %s" % (row['Title'], row['Artist']) result = mb.search_recordings(row['Title'], artist = row['Artist'], status = 'official', strict = True, limit = 25) rel_date = None if verbose: if 'recording-list' in result: print "found %d recordings" % len(result['recording-list']) else: print "no recording-list found" for recording in result['recording-list']: if verbose: print 'id: %s artist %s' % \ (recording['id'], recording['artist-credit'][0]['artist']['sort-name'].encode('utf-8')) if recording['release-list']: print "part of %d releases" % len(recording['release-list']) else: print "no releases" if recording['release-list']: for release in recording['release-list']: if verbose: if 'date' in release and len(release['date']) > 0: d = release['date'] y = int(release['date'].split('-')[0]) if y > 1979 and (rel_date is None or rel_date > y): rel_date = y else: d = "N/A" if verbose: print 'release id: %s date %s title %s' % \ (release['id'], d, release['title'].encode('utf-8')) if verbose: if rel_date is not None: print "computed release year of %d" % rel_date else: print 'no release date found'
def Tag(self, song): try: res = musicbrainzngs.search_recordings(limit = 1, query = song['title'], artist = song['artist'], release = song['album'], qdur = str(song['duration'] * 1000))['recording-list'][0] song['number'] = int(res['release-list'][0]['medium-list'][1]['track-list'][0]['number']) song['count'] = res['release-list'][0]['medium-list'][1]['track-count'] song['score'] = res['ext:score'] song['brain'] = res['id'] except: song['score'] = '0' Log("Tag%4s%%" % song['score'], song) return song['score'] == '100'
def get_url(artist, title): if artist is None or title is None: return None payload = {'func': 'getSong', 'artist': artist, 'song': title, 'fmt': 'realjson'} try: r = requests.get("http://lyrics.wikia.com/api.php", params=payload) except: url = None else: q = r.json() url = q['url'] if 'url' in q else None if url and url.find("action=edit") != -1: url = None if url is not None: return url try: z = musicbrainzngs.search_recordings(artist=artist, recording=title, limit=5, offset=None, strict=False) except: return None for d in z['recording-list']: if int(d['ext:score'])==100: new_artist = d['artist-credit-phrase'] new_title = d['title'] payload = {'func': 'getSong', 'artist': new_artist, 'song': new_title, 'fmt': 'realjson'} try: r = requests.get("http://lyrics.wikia.com/api.php", params=payload) except: return None q = r.json() url = q['url'] if 'url' in q else None if url and url.find("action=edit") != -1: url = None else: print "got song lyrics by using musicbrainz db to figure out correct artist and title" return url
def getMusicBrainzId(artist, album="", track=""): albumid = "" artistid = "" album = album.replace(" (single)","") track = track.replace(" (edit)","").replace(" (radio edit)","") logMsg("getMusicBrainzId -- artist: - %s - album: %s - track: %s" %(artist,album,track)) try: if not WINDOW.getProperty("SkinHelper.TempDisableMusicBrainz"): MBalbum = None if artist and album: MBalbums = m.search_release_groups(query=single_urlencode(try_encode(album)),limit=1,offset=None, strict=False, artist=single_urlencode(try_encode(artist))) if MBalbums and MBalbums.get("release-group-list"): MBalbum = MBalbums.get("release-group-list")[0] if not MBalbum and artist and track: MBalbums = m.search_recordings(query=single_urlencode(try_encode(track)),limit=1,offset=None, strict=False, artist=single_urlencode(try_encode(artist))) if MBalbums and MBalbums.get("recording-list"): MBalbum = MBalbums.get("recording-list")[0] if MBalbum: albumid = MBalbum.get("id","") MBartist = MBalbum.get("artist-credit")[0] artistid = MBartist.get("artist").get("id") except Exception as e: logMsg("MusicBrainz ERROR (servers busy?) - temporary disabling musicbrainz lookups (fallback to theaudiodb)", 0) WINDOW.setProperty("SkinHelper.TempDisableMusicBrainz","disable") #use theaudiodb as fallback try: if not artistid and artist and album: audiodb_url = 'http://www.theaudiodb.com/api/v1/json/193621276b2d731671156g/searchalbum.php' params = {'s' : artist, 'a': album} response = requests.get(audiodb_url, params=params) if response and response.content: data = json.loads(response.content.decode('utf-8','replace')) if data and data.get("album") and len(data.get("album")) > 0: adbdetails = data["album"][0] albumid = adbdetails.get("strMusicBrainzID") artistid = adbdetails.get("strMusicBrainzArtistID") elif not artistid and artist and track: audiodb_url = 'http://www.theaudiodb.com/api/v1/json/193621276b2d731671156g/searchtrack.php' params = {'s' : artist, 't': track} response = requests.get(audiodb_url, params=params) if response and response.content: data = json.loads(response.content.decode('utf-8','replace')) if data and data.get("track") and len(data.get("track")) > 0: adbdetails = data["track"][0] albumid = adbdetails.get("strMusicBrainzAlbumID") artistid = adbdetails.get("strMusicBrainzArtistID") except Exception as e: logMsg("getMusicArtworkByDbId AudioDb lookup failed --> " + str(e), 0) return {} logMsg("getMusicBrainzId results for artist %s - artistid: %s - albumid: %s" %(artist,artistid,albumid)) return (artistid, albumid)
def get_remote_data(self): """Sends a query corresponding to the title/artist of the track to the MusicBrainz server. Careful : each query can take a few seconds.""" query = "" # No query if not data was found. if self.localData.title == "": return # Adding details to the query. query += self.localData.title if self.localData.artist != "": query += ",artist:%s" %self.localData.artist # Sending it. results = mbngs.search_recordings(query, limit=10) return results
def match_track(artist, title, limit=SEARCH_LIMIT): """Searches for a single track and returns an iterable of TrackInfo objects. May raise a MusicBrainzAPIError. """ criteria = {"artist": artist.lower(), "recording": title.lower()} if not any(criteria.itervalues()): return try: res = musicbrainzngs.search_recordings(limit=limit, **criteria) except musicbrainzngs.MusicBrainzError as exc: raise MusicBrainzAPIError(exc, "recording search", criteria, traceback.format_exc()) for recording in res["recording-list"]: yield track_info(recording)
def track_data(trackname, artistname=None, releaseid=None): try: query = "recording:\"{}\"".format(trackname) if releaseid is not None: data = musicbrainzngs.search_recordings(query=query, reid=releaseid, limit=1, strict=False) elif artistname is not None: data = musicbrainzngs.search_recordings(query=query, artistname=artistname, limit=1, strict=False) else: data = musicbrainzngs.search_recordings(query=query, limit=1, strict=False) if len(data["recording-list"]) >= 1: return data["recording-list"][0] except Exception as e: logging.warning("error while loading musicbrainz data for %s: %s", trackname, e)
def com_mediabrainz_get_recordings(self, artist_name=None, release_name=None, song_name=None, return_limit=5, strict_flag=False): """ # search by artist and song name """ result = musicbrainzngs.search_recordings(artist=artist_name, release=release_name, recording=song_name, limit=return_limit, strict=strict_flag) if not result['recording-list']: common_global.es_inst.com_elastic_index('error', {'stuff': "no recording found"}) return None else: for (idx, release) in enumerate(result['recording-list']): common_global.es_inst.com_elastic_index('info', {"match #{}:".format(idx + 1)}) self.show_release_details(release) return release['id']
def search_musicbrainz(self, song_name): info = self.song_info if info['title'] == '' or 'Track' in info['title']: # Converting song_name into lower case so that we don't have to # search for all .MP3 combination like(mp3,mP3,Mp3,MP3) song_name = song_name.lower() if '.mp3' in song_name: pos = song_name.find('.mp3') info['title'] = song_name[:pos] # Finding song information in DB of music brainz and taking only 1 # result in response by using limit=1 self.data = mbz.search_recordings(query=info['title'], limit=1, artist=info['artist'], release=info['album'], date=str(info['year']), qdur=str(info['duration']) )
def findMatchMB(text, scoreComposer, searchString, scoreid): results = musicbrainzngs.search_recordings(query=text, limit=5) # remove title from search string lText = text.lower() lSearchString = searchString.lower() regex = re.compile(r'\b('+text+r')\b', flags=re.IGNORECASE) lSearchString = regex.sub("", lSearchString) for i, t in enumerate(results['recording-list']): #print t['artist-credit'][0]['artist']['name'] artist = t['artist-credit'][0]['artist']['name'] lArtist = artist.lower() if lSearchString.find(lArtist) >= 0 or (scoreComposer != "" and lArtist.find(scoreComposer) >= 0) : print "MATCH ", scoreid, t['title'] + " - " + artist #print "MATCH artist " + t['artists'][0]['uri'] #print "MATCH track " + t['uri'] return (scoreid, artist.encode("utf-8"), t['artist-credit'][0]['artist']['uri'], t['title'].encode("utf-8"), t['uri']) return None
def get_metadata(artist_title): temp_results = [] results = [] search_results = musicbrainzngs.search_recordings(recording=artist_title[1], artist=artist_title[0], limit=50) for recording in search_results['recording-list']: if artist_title[0].replace(' ', '').lower() in recording['artist-credit'][0]['artist']['name'].replace(' ', '').lower() or recording['artist-credit'][0]['artist']['name'].replace(' ', '').lower() in artist_title[0].replace(' ', '').lower(): if 'release-list' in recording: for release in recording['release-list']: artist = recording['artist-credit'][0]['artist']['name'] title = recording['title'] album = release['title'] id = release['id'] entry = [artist, title, album, id] if entry[:3] not in temp_results and valid(recording, release, entry): temp_results.append(entry[:3]) results.append(entry) return results
def test_search_recordings(self): musicbrainzngs.search_recordings("Thief of Hearts") self.assertEqual("http://musicbrainz.org/ws/2/recording/?query=Thief+of+Hearts", self.opener.get_url()) musicbrainzngs.search_recordings(recording="Thief of Hearts") expected_query = 'recording:(thief of hearts)' expected = 'http://musicbrainz.org/ws/2/recording/?query=%s' % musicbrainzngs.compat.quote_plus(expected_query) self.assertEquals(expected, self.opener.get_url()) # Invalid query field with self.assertRaises(musicbrainzngs.InvalidSearchFieldError): musicbrainzngs.search_recordings(foo="value")
def panTag(song, path): tag = MP4(path) dur = str(int(tag.info.length * 1000)) res = _brain.search_recordings(limit = 1, query = song.title, artist = song.artist, release = song.album, qdur = dur)['recording-list'][0] sco = res['ext:score'] if sco == '100': tag['----:com.apple.iTunes:MusicBrainz Track Id'] = res['id'] tag['\xa9ART'] = song.artist tag['\xa9alb'] = song.album tag['\xa9nam'] = song.title tag.save() xbmc.log("%s.Tag OK (%13s,%4s %%) '%s - %s - %s'" % (_plugin, _stamp, sco, song.songId[:4], song.artist, song.title)) return True else: xbmc.log("%s.Tag FAIL (%13s,%4s %%) '%s - %s - %s'" % (_plugin, _stamp, sco, song.songId[:4], song.artist, song.title)) return False
def Tag(self, song): try: res = musicbrainzngs.search_recordings( limit=1, query=song["title"], artist=song["artist"], release=song["album"], qdur=str(song["duration"] * 1000), )["recording-list"][0] song["number"] = int(res["release-list"][0]["medium-list"][1]["track-list"][0]["number"]) song["count"] = res["release-list"][0]["medium-list"][1]["track-count"] song["score"] = res["ext:score"] song["brain"] = res["id"] except: song["score"] = "0" Log("Tag%4s%%" % song["score"], song) return song["score"] == "100"
def match_track(artist, title): """Searches for a single track and returns an iterable of TrackInfo objects. May raise a MusicBrainzAPIError. """ criteria = { 'artist': artist.lower().strip(), 'recording': title.lower().strip(), } if not any(criteria.values()): return try: res = musicbrainzngs.search_recordings( limit=config['musicbrainz']['searchlimit'].get(int), **criteria) except musicbrainzngs.MusicBrainzError as exc: raise MusicBrainzAPIError(exc, 'recording search', criteria, traceback.format_exc()) for recording in res['recording-list']: yield track_info(recording)
def search_musicbrainz(self, song_name): """ Searching music brainz db for particular song """ try: info = self.song_info if info['title'] == '' or 'Track' in info['title']: # Converting song_name into lower case so that we don't have to # search for all .MP3 combination like(mp3,mP3,Mp3,MP3) song_name = song_name.lower() if '.mp3' in song_name: pos = song_name.find('.mp3') info['title'] = song_name[:pos] # Finding song information in DB of music brainz and taking only 1 # result in response by using limit=1 self.data = mbz.search_recordings(query=info['title'], limit=1, artist=info['artist'], release=info['album'], date=str(info['year']), qdur=str(info['duration']) ) except: eg.msgbox("Please Check your Internet Connection!") return 1
def get_release_date(artist, album, title): try: result = musicbrainzngs.search_releases(artist=artist, release=album, limit=20, strict=True) except: return "No date cxception (search_releases)" #release_list = result['release-list'] # can be missing if 'release-list' in result: release_list = result['release-list'] # can be missing dates = [d['date'][0:4] for d in release_list if 'date' in d and int(d['ext:score']) > 90] if dates: dates.sort() return dates[0] # above may not work if it's a collection album with a made up name; the below tries to address that try: result = musicbrainzngs.search_recordings(artist=artist, recording=title, limit=20, offset=None, strict=False) except: return "No date exception (search_recordings)" recording_list = result['recording-list'] dates = [] for d in recording_list: if 'release-list' in d: dd = [x['date'][0:4]+': '+x['title'] for x in d['release-list'] if 'date' in x and int(d['ext:score']) > 90] dates.extend(dd) #[item for sublist in l for item in sublist] - this should work but not sure it makes sense to modify above which works if dates: dates.sort() return dates[0] else: return "No date"
def process_query(self, query): """Process a search query and add any lyrics that result to the database. """ # Check if the query has been recently seen. if (SeenQuery.objects(text=query)): # If it has just return return # Otherwise cache the query now SeenQuery(text=' '.join(sorted(query.lower().split(' ')))).save() response = mb.search_recordings(query=query)['recording-list'] results = [Song( title=x['title'], artist=x['artist-credit-phrase'], mbid=x['id'] ) for x in response] results = set(results) for song in results: self.process_song(song)
def getAlbumArtistNames(album,artist, apihandle, song=None): ''' Given a supposed album and artist, determine the real ones ''' def searchWhatAlbums(args): if len(args)==0: return [] whatResponse = apihandle.request(action='browse',searchstr=args[0]) if whatResponse['status']=='success': args.pop(0) return searchWhatAlbums(args)+[x for x in whatResponse['response']['results'] if 'artist' in x and 'groupName' in x] return [] mbAlbums = [] parens = re.compile('[\(\[].*[\)\]]') if song is not None: includes = ['recordings'] artists = set(re.split(' &|and|ft\.?|featuring|feat\.? ',artist)) if len(artists) > 1: artists.add(artist) for ar in artists: mbAlbums += [dict(list(x.items())+[("artist-credit-phrase", releases['artist-credit-phrase'])]) for releases in mb.search_recordings(query=song, limit=int(50*mbMultiplier), artistname=mbquote(ar), release=mbquote(album))['recording-list'] for x in releases['release-list']] mbAlbums += mb.search_releases(artist=mbquote(ar),query=mbquote(album),limit=int(25*mbMultiplier))['release-list'] lastfmres = [x['mbid'] for x in lookup('lastfm','songsearch',{'artist':ar, 'song':song})['results']['trackmatches']['track'] if 'mbid' in x and len(x['mbid'])>0] if len(lastfmres)>0: for lastfmRecId in set(lastfmres): try: lastfmAlbum = mb.get_recording_by_id(id=lastfmRecId,includes=['releases','artist-credits']) for alb in lastfmAlbum['recording'].pop('release-list'): alb['medium-list'] = [{}] alb['medium-list'][0]['track-list'] = [] alb['medium-list'][0]['track-list'].append(lastfmAlbum) alb['artist-credit-phrase'] = lastfmAlbum['recording']['artist-credit-phrase'] mbAlbums.append(alb) except Exception as e: print(e) else: includes = [] mbArtists = mb.search_artists(query=mbquote(artist),limit=int(40*mbMultiplier))['artist-list'] mbAlbums += mb.search_releases(artist=mbquote(artist),query=mbquote(album),limit=int(50*mbMultiplier))['release-list'] for mbArtist in mbArtists: if Levenshtein.ratio(artist.lower(),mbArtist['name'].lower()) > 0.75: mbAlbums+=[ dict(list(x.items())+[('artist-credit-phrase',mbArtist['name'])]) for x in mb.browse_releases(artist=mbArtist['id'],includes=includes,limit=25)['release-list']] if (len(album)<7 and ('/' in album or ' & ' in album) and 's' in album.lower() and 't' in album.lower()) or ('self' in album.lower() and 'titled' in album.lower()): mbAlbums += mb.search_releases(artist=mbquote(artist),query=mbquote(artist),limit=int(25*mbMultiplier))['release-list'] temp = [] for x in mbAlbums[:]: if x["id"] in temp and not ('medium-list' in x and len(x['medium-list'])>0 and all('track-list' in z and len(z['track-list'])>0 for z in x['medium-list'])): mbAlbums.remove(x) else: temp.append(x['id']) print("Done searching musicbrainz for album suggestions, have "+str(len(mbAlbums))+" to rank") ranks = {} for x in mbAlbums: ranks[x['id']] = Levenshtein.ratio(album.lower(),x['title'].lower()) if song is not None: x['song'] = {} temp = ([(y['recording']['title'] if 'recording' in y else y['title'], int(float( y['recording']['length'] if 'recording' in y and 'length' in y['recording'] else (y['track_or_recording_length'] if 'track_or_recording_length' in x else y['length'] if 'length' in x else 0) )/1000.)) for tracklist in x['medium-list'] for y in tracklist['track-list']] if 'medium-list' in x and len(x['medium-list'])>0 and all('track-list' in z and len(z['track-list'])>0 for z in x['medium-list']) else getSongs( {"artist":x['artist-credit-phrase'], "groupName":x['title']})) x['song']['name'], x['song']['duration'] = (max(temp, key=lambda y: Levenshtein.ratio(y[0].lower(),song.lower())) if len(temp)>0 else ("",-1)) if ranks[x['id']] < Levenshtein.ratio(x['song']['name'].lower(),song.lower()): ranks[x['id']] /= 6 ranks[x['id']] += (Levenshtein.ratio(x['song']['name'].lower(),song.lower()) + Levenshtein.ratio(parens.sub('',x['song']['name'].lower()),parens.sub('',song.lower())) )*5/12 else: ranks[x['id']] /= 3 ranks[x['id']] += (Levenshtein.ratio(x['song']['name'].lower(),song.lower()) + Levenshtein.ratio(parens.sub('',x['song']['name'].lower()),parens.sub('',song.lower())) )/3 ranks[x['id']] += Levenshtein.ratio(artist.lower(),x['artist-credit-phrase'].lower())*7/6 if len(ranks) == 0: return None mbAlbumId, mbAlbumRank = max(ranks.items(),key=(lambda x:x[1])) mbAlbum = [x for x in mbAlbums if x['id']==mbAlbumId][0] print("For the artist and album derived from the provided dir ("+artist+" and "+album+" respectively),\nthe following artist and album was matched on musicbrains:") print("Artist: "+mbAlbum['artist-credit-phrase']) print("Album: "+mbAlbum['title']) whatAlbums = searchWhatAlbums([mbAlbum['title']+' '+mbAlbum['artist-credit-phrase'], artist+' '+album]) if len(whatAlbums) == 0: whatAlbums = searchWhatAlbums([artist,album,mbAlbum['title'],mbAlbum['artist-credit-phrase']]) if len(whatAlbums) == 0: return None whatAlbums = sorted(whatAlbums, key=(lambda x: Levenshtein.ratio(x['groupName'],mbAlbum['title']) +Levenshtein.ratio(x['groupName'].lower(),album.lower())*3/8 +Levenshtein.ratio(x['artist'],mbAlbum['artist-credit-phrase']) +Levenshtein.ratio(x['artist'].lower(),artist.lower())*5/8), reverse=True)#[:min(10,len(whatAlbums))] whatAlbum = whatAlbums[0] whatAlbum['artist-credit-phrase'] = mbAlbum['artist-credit-phrase'] if song is not None: whatAlbum['song'] = mbAlbum['song'] print("For the album and artist found on musicbrainz, the following torrent group was found on what:") print("Artist: "+whatAlbum['artist']) print("Album: "+whatAlbum['groupName']) return whatAlbum
def testSearchRecording(self): musicbrainzngs.search_recordings("Thief of Hearts") self.assertEqual("http://musicbrainz.org/ws/2/recording/?query=Thief+of+Hearts", self.opener.get_url())