def load_similar_tracks(self, file_): """Perform search using existing metadata information from the file as query.""" self.retry_params = Retry(self.load_similar_tracks, file_) self.file_ = file_ metadata = file_.orig_metadata query = { 'track': metadata['title'], 'artist': metadata['artist'], 'release': metadata['album'], 'tnum': metadata['tracknumber'], 'tracks': metadata['totaltracks'], 'qdur': str(metadata.length // 2000), 'isrc': metadata['isrc'], } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only track title. if self.use_advanced_search: query_str = ' '.join([ '%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value ]) else: query_str = query["track"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_tracks(self.handle_reply, **query)
def show_similar_albums(self, cluster): """Perform search by using existing metadata information from the cluster as query.""" self.retry_params = Retry(self.show_similar_albums, cluster) self.cluster = cluster metadata = cluster.metadata query = { "artist": metadata["albumartist"], "release": metadata["album"], "tracks": str(len(cluster.files)) } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only album title. if self.use_advanced_search: query_str = ' '.join(['%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value]) else: query_str = query["release"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_releases( self.handle_reply, **query)
def search(self, text): self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_artists(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT)
def search(self, text): """Perform search using query provided by the user.""" self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_tracks(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT)
def show_similar_albums(self, cluster): """Perform search by using existing metadata information from the cluster as query.""" self.retry_params = Retry(self.show_similar_albums, cluster) self.cluster = cluster metadata = cluster.metadata query = { "artist": metadata["albumartist"], "release": metadata["album"], "tracks": str(len(cluster.files)) } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only album title. if config.setting["use_adv_search_syntax"]: query_str = ' '.join(['%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value]) else: query_str = query["release"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_releases( self.handle_reply, **query)
def load_similar_tracks(self, file_): """Perform search using existing metadata information from the file as query.""" self.retry_params = Retry(self.load_similar_tracks, file_) self.file_ = file_ metadata = file_.orig_metadata query = { 'track': metadata['title'], 'artist': metadata['artist'], 'release': metadata['album'], 'tnum': metadata['tracknumber'], 'tracks': metadata['totaltracks'], 'qdur': str(metadata.length // 2000), 'isrc': metadata['isrc'], } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only track title. if config.setting["use_adv_search_syntax"]: query_str = ' '.join(['%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value]) else: query_str = query["track"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_tracks( self.handle_reply, **query)
def search(self, text): self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_artists(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT)
def search(self, text): """Perform search using query provided by the user.""" self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_tracks(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT)
class TrackSearchDialog(SearchDialog): dialog_header_state = "tracksearchdialog_header_state" options = [Option("persist", dialog_header_state, QtCore.QByteArray())] def __init__(self, parent): super().__init__(parent, accept_button_title=_("Load into Picard"), search_type="track") self.file_ = None self.setWindowTitle(_("Track Search Results")) self.columns = [ ('name', _("Name")), ('length', _("Length")), ('artist', _("Artist")), ('release', _("Release")), ('date', _("Date")), ('country', _("Country")), ('type', _("Type")), ('score', _("Score")), ] def search(self, text): """Perform search using query provided by the user.""" self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_tracks( self.handle_reply, query=text, search=True, advanced_search=self.use_advanced_search, limit=QUERY_LIMIT) def load_similar_tracks(self, file_): """Perform search using existing metadata information from the file as query.""" self.retry_params = Retry(self.load_similar_tracks, file_) self.file_ = file_ metadata = file_.orig_metadata query = { 'track': metadata['title'], 'artist': metadata['artist'], 'release': metadata['album'], 'tnum': metadata['tracknumber'], 'tracks': metadata['totaltracks'], 'qdur': str(metadata.length // 2000), 'isrc': metadata['isrc'], } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only track title. if self.use_advanced_search: query_str = ' '.join([ '%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value ]) else: query_str = query["track"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_tracks(self.handle_reply, **query) def retry(self): self.retry_params.function(self.retry_params.query) def handle_reply(self, document, http, error): if error: self.network_error(http, error) return try: tracks = document['recordings'] except (KeyError, TypeError): self.no_results_found() return if self.file_: metadata = self.file_.orig_metadata def candidates(): for track in tracks: yield metadata.compare_to_track(track, File.comparison_weights) tracks = [ result.track for result in sort_by_similarity(candidates) ] del self.search_results[:] # Clear existing data self.parse_tracks(tracks) self.display_results() def display_results(self): self.prepare_table() for row, obj in enumerate(self.search_results): track = obj[0] self.table.insertRow(row) self.set_table_item(row, 'name', track, "title") self.set_table_item(row, 'length', track, "~length", sortkey=track.length) self.set_table_item(row, 'artist', track, "artist") self.set_table_item(row, 'release', track, "album") self.set_table_item(row, 'date', track, "date") self.set_table_item(row, 'country', track, "country") self.set_table_item(row, 'type', track, "releasetype") self.set_table_item(row, 'score', track, "score") self.show_table(sort_column='score') def parse_tracks(self, tracks): for node in tracks: if "releases" in node: for rel_node in node['releases']: track = Metadata() recording_to_metadata(node, track) track['score'] = node['score'] release_to_metadata(rel_node, track) rg_node = rel_node['release-group'] release_group_to_metadata(rg_node, track) countries = countries_from_node(rel_node) if countries: track["country"] = ", ".join(countries) self.search_results.append((track, node)) else: # This handles the case when no release is associated with a track # i.e. the track is an NAT track = Metadata() recording_to_metadata(node, track) track['score'] = node['score'] track["album"] = _("Standalone Recording") self.search_results.append((track, node)) def accept_event(self, rows): for row in rows: self.load_selection(row) def load_selection(self, row): """Load the album corresponding to the selected track. If the search is performed for a file, also associate the file to corresponding track in the album. """ track, node = self.search_results[row] if track.get("musicbrainz_albumid"): # The track is not an NAT self.tagger.get_release_group_by_id( track["musicbrainz_releasegroupid"]).loaded_albums.add( track["musicbrainz_albumid"]) if self.file_: # Search is performed for a file. # Have to move that file from its existing album to the new one. if isinstance(self.file_.parent, Track): album = self.file_.parent.album self.tagger.move_file_to_track( self.file_, track["musicbrainz_albumid"], track["musicbrainz_recordingid"]) if album.get_num_total_files() == 0: # Remove album if it has no more files associated self.tagger.remove_album(album) else: self.tagger.move_file_to_track( self.file_, track["musicbrainz_albumid"], track["musicbrainz_recordingid"]) else: # No files associated. Just a normal search. self.tagger.load_album(track["musicbrainz_albumid"]) else: if self.file_ and getattr(self.file_.parent, 'album', None): album = self.file_.parent.album self.tagger.move_file_to_nat(self.file_, track["musicbrainz_recordingid"], node) if album.get_num_total_files() == 0: self.tagger.remove_album(album) else: self.tagger.load_nat(track["musicbrainz_recordingid"], node) self.tagger.move_file_to_nat(self.file_, track["musicbrainz_recordingid"], node)
class AlbumSearchDialog(SearchDialog): dialog_header_state = "albumsearchdialog_header_state" options = [ config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] def __init__(self, parent, force_advanced_search=None): super().__init__( parent, accept_button_title=_("Load into Picard"), search_type="album", force_advanced_search=force_advanced_search) self.cluster = None self.setWindowTitle(_("Album Search Results")) self.columns = [ ('name', _("Name")), ('artist', _("Artist")), ('format', _("Format")), ('tracks', _("Tracks")), ('date', _("Date")), ('country', _("Country")), ('labels', _("Labels")), ('catnums', _("Catalog #s")), ('barcode', _("Barcode")), ('language', _("Language")), ('type', _("Type")), ('status', _("Status")), ('cover', _("Cover")), ('score', _("Score")), ] self.cover_cells = [] self.fetching = False self.scrolled.connect(self.fetch_coverarts) def search(self, text): """Perform search using query provided by the user.""" self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_releases(self.handle_reply, query=text, search=True, advanced_search=self.use_advanced_search, limit=QUERY_LIMIT) def show_similar_albums(self, cluster): """Perform search by using existing metadata information from the cluster as query.""" self.retry_params = Retry(self.show_similar_albums, cluster) self.cluster = cluster metadata = cluster.metadata query = { "artist": metadata["albumartist"], "release": metadata["album"], "tracks": str(len(cluster.files)) } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only album title. if self.use_advanced_search: query_str = ' '.join(['%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value]) else: query_str = query["release"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_releases( self.handle_reply, **query) def retry(self): self.retry_params.function(self.retry_params.query) def handle_reply(self, document, http, error): if error: self.network_error(http, error) return try: releases = document['releases'] except (KeyError, TypeError): self.no_results_found() return del self.search_results[:] self.parse_releases(releases) self.display_results() self.fetch_coverarts() def fetch_coverarts(self): if self.fetching: return self.fetching = True for cell in self.cover_cells: self.fetch_coverart(cell) self.fetching = False def fetch_coverart(self, cell): """Queue cover art jsons from CAA server for each album in search results. """ if cell.fetched: return if not cell.is_visible(): return cell.fetched = True caa_path = "/release/%s" % cell.release["musicbrainz_albumid"] cell.fetch_task = self.tagger.webservice.get( CAA_HOST, CAA_PORT, caa_path, partial(self._caa_json_downloaded, cell) ) def _caa_json_downloaded(self, cover_cell, data, http, error): """Handle json reply from CAA server. If server replies without error, try to get small thumbnail of front coverart of the release. """ cover_cell.fetch_task = None if error: cover_cell.not_found() return front = None try: for image in data["images"]: if image["front"]: front = image break if front: url = front["thumbnails"]["small"] coverartimage = CaaThumbnailCoverArtImage(url=url) cover_cell.fetch_task = self.tagger.webservice.download( coverartimage.host, coverartimage.port, coverartimage.path, partial(self._cover_downloaded, cover_cell) ) else: cover_cell.not_found() except (AttributeError, KeyError, TypeError): log.error("Error reading CAA response", exc_info=True) cover_cell.not_found() def _cover_downloaded(self, cover_cell, data, http, error): """Handle cover art query reply from CAA server. If server returns the cover image successfully, update the cover art cell of particular release. Args: row -- Album's row in results table """ cover_cell.fetch_task = None if error: cover_cell.not_found() else: pixmap = QtGui.QPixmap() try: pixmap.loadFromData(data) cover_cell.set_pixmap(pixmap) except Exception as e: cover_cell.not_found() log.error(e) def fetch_cleanup(self): for cell in self.cover_cells: if cell.fetch_task is not None: log.debug("Removing cover art fetch task for %s", cell.release['musicbrainz_albumid']) self.tagger.webservice.remove_task(cell.fetch_task) def closeEvent(self, event): if self.cover_cells: self.fetch_cleanup() super().closeEvent(event) def parse_releases(self, releases): for node in releases: release = Metadata() release_to_metadata(node, release) release['score'] = node['score'] rg_node = node['release-group'] release_group_to_metadata(rg_node, release) if "media" in node: media = node['media'] release["format"] = media_formats_from_node(media) release["tracks"] = node['track-count'] countries = countries_from_node(node) if countries: release["country"] = ", ".join(countries) self.search_results.append(release) def display_results(self): self.prepare_table() self.cover_cells = [] for row, release in enumerate(self.search_results): self.table.insertRow(row) self.set_table_item(row, 'name', release, "album") self.set_table_item(row, 'artist', release, "albumartist") self.set_table_item(row, 'format', release, "format") self.set_table_item(row, 'tracks', release, "tracks") self.set_table_item(row, 'date', release, "date") self.set_table_item(row, 'country', release, "country") self.set_table_item(row, 'labels', release, "label") self.set_table_item(row, 'catnums', release, "catalognumber") self.set_table_item(row, 'barcode', release, "barcode") self.set_table_item(row, 'language', release, "~releaselanguage") self.set_table_item(row, 'type', release, "releasetype") self.set_table_item(row, 'status', release, "releasestatus") self.set_table_item(row, 'score', release, "score") self.cover_cells.append(CoverCell(self, release, row, 'cover', on_show=self.fetch_coverart)) self.show_table(sort_column='score') def accept_event(self, rows): for row in rows: self.load_selection(row) def load_selection(self, row): release = self.search_results[row] self.tagger.get_release_group_by_id( release["musicbrainz_releasegroupid"]).loaded_albums.add( release["musicbrainz_albumid"]) album = self.tagger.load_album(release["musicbrainz_albumid"]) if self.cluster: files = self.cluster.iterfiles() self.tagger.move_files_to_album(files, release["musicbrainz_albumid"], album)
class ArtistSearchDialog(SearchDialog): dialog_header_state = "artistsearchdialog_header_state" options = [ config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] def __init__(self, parent): super().__init__(parent, accept_button_title=_("Show in browser"), search_type="artist") self.setWindowTitle(_("Artist Search Dialog")) self.columns = [ ('name', _("Name")), ('type', _("Type")), ('gender', _("Gender")), ('area', _("Area")), ('begindate', _("Begin")), ('beginarea', _("Begin Area")), ('enddate', _("End")), ('endarea', _("End Area")), ('score', _("Score")), ] def search(self, text): self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_artists(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT) def retry(self): self.retry_params.function(self.retry_params.query) def handle_reply(self, document, http, error): if error: self.network_error(http, error) return try: artists = document['artists'] except (KeyError, TypeError): self.no_results() return del self.search_results[:] self.parse_artists(artists) self.display_results() def parse_artists(self, artists): for node in artists: artist = Metadata() artist_to_metadata(node, artist) artist['score'] = node['score'] self.search_results.append(artist) def display_results(self): self.prepare_table() for row, artist in enumerate(self.search_results): self.table.insertRow(row) self.set_table_item(row, 'name', artist, "name") self.set_table_item(row, 'type', artist, "type") self.set_table_item(row, 'gender', artist, "gender") self.set_table_item(row, 'area', artist, "area") self.set_table_item(row, 'begindate', artist, "begindate") self.set_table_item(row, 'beginarea', artist, "beginarea") self.set_table_item(row, 'enddate', artist, "enddate") self.set_table_item(row, 'endarea', artist, "endarea") self.set_table_item(row, 'score', artist, "score") self.show_table(sort_column='score') def accept_event(self, rows): for row in rows: self.load_in_browser(row) def load_in_browser(self, row): self.tagger.search(self.search_results[row]["musicbrainz_artistid"], "artist")
class TrackSearchDialog(SearchDialog): dialog_header_state = "tracksearchdialog_header_state" options = [ config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] def __init__(self, parent): super().__init__( parent, accept_button_title=_("Load into Picard"), search_type="track") self.file_ = None self.setWindowTitle(_("Track Search Results")) self.columns = [ ('name', _("Name")), ('length', _("Length")), ('artist', _("Artist")), ('release', _("Release")), ('date', _("Date")), ('country', _("Country")), ('type', _("Type")), ('score', _("Score")), ] def search(self, text): """Perform search using query provided by the user.""" self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_tracks(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT) def load_similar_tracks(self, file_): """Perform search using existing metadata information from the file as query.""" self.retry_params = Retry(self.load_similar_tracks, file_) self.file_ = file_ metadata = file_.orig_metadata query = { 'track': metadata['title'], 'artist': metadata['artist'], 'release': metadata['album'], 'tnum': metadata['tracknumber'], 'tracks': metadata['totaltracks'], 'qdur': str(metadata.length // 2000), 'isrc': metadata['isrc'], } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only track title. if config.setting["use_adv_search_syntax"]: query_str = ' '.join(['%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value]) else: query_str = query["track"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_tracks( self.handle_reply, **query) def retry(self): self.retry_params.function(self.retry_params.query) def handle_reply(self, document, http, error): if error: self.network_error(http, error) return try: tracks = document['recordings'] except (KeyError, TypeError): self.no_results_found() return if self.file_: metadata = self.file_.orig_metadata def candidates(): for track in tracks: yield metadata.compare_to_track(track, File.comparison_weights) tracks = [result.track for result in sort_by_similarity(candidates)] del self.search_results[:] # Clear existing data self.parse_tracks(tracks) self.display_results() def display_results(self): self.prepare_table() for row, obj in enumerate(self.search_results): track = obj[0] self.table.insertRow(row) self.set_table_item(row, 'name', track, "title") self.set_table_item(row, 'length', track, "~length", sort=BY_DURATION) self.set_table_item(row, 'artist', track, "artist") self.set_table_item(row, 'release', track, "album") self.set_table_item(row, 'date', track, "date") self.set_table_item(row, 'country', track, "country") self.set_table_item(row, 'type', track, "releasetype") self.set_table_item(row, 'score', track, "score", sort=BY_NUMBER) self.show_table(sort_column='score') def parse_tracks(self, tracks): for node in tracks: if "releases" in node: for rel_node in node['releases']: track = Metadata() recording_to_metadata(node, track) track['score'] = node['score'] release_to_metadata(rel_node, track) rg_node = rel_node['release-group'] release_group_to_metadata(rg_node, track) countries = countries_from_node(rel_node) if countries: track["country"] = ", ".join(countries) self.search_results.append((track, node)) else: # This handles the case when no release is associated with a track # i.e. the track is an NAT track = Metadata() recording_to_metadata(node, track) track['score'] = node['score'] track["album"] = _("Standalone Recording") self.search_results.append((track, node)) def accept_event(self, arg): self.load_selection(arg) def load_selection(self, row): """Load the album corresponding to the selected track. If the search is performed for a file, also associate the file to corresponding track in the album. """ track, node = self.search_results[row] if track.get("musicbrainz_albumid"): # The track is not an NAT self.tagger.get_release_group_by_id(track["musicbrainz_releasegroupid"]).loaded_albums.add( track["musicbrainz_albumid"]) if self.file_: # Search is performed for a file. # Have to move that file from its existing album to the new one. if isinstance(self.file_.parent, Track): album = self.file_.parent.album self.tagger.move_file_to_track(self.file_, track["musicbrainz_albumid"], track["musicbrainz_recordingid"]) if album._files == 0: # Remove album if it has no more files associated self.tagger.remove_album(album) else: self.tagger.move_file_to_track(self.file_, track["musicbrainz_albumid"], track["musicbrainz_recordingid"]) else: # No files associated. Just a normal search. self.tagger.load_album(track["musicbrainz_albumid"]) else: if self.file_ and getattr(self.file_.parent, 'album', None): album = self.file_.parent.album self.tagger.move_file_to_nat(self.file_, track["musicbrainz_recordingid"], node) if album._files == 0: self.tagger.remove_album(album) else: self.tagger.load_nat(track["musicbrainz_recordingid"], node) self.tagger.move_file_to_nat(self.file_, track["musicbrainz_recordingid"], node)
class AlbumSearchDialog(SearchDialog): dialog_header_state = "albumsearchdialog_header_state" options = [ config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] def __init__(self, parent): super().__init__( parent, accept_button_title=_("Load into Picard"), search_type="album") self.cluster = None self.setWindowTitle(_("Album Search Results")) self.columns = [ ('name', _("Name")), ('artist', _("Artist")), ('format', _("Format")), ('tracks', _("Tracks")), ('date', _("Date")), ('country', _("Country")), ('labels', _("Labels")), ('catnums', _("Catalog #s")), ('barcode', _("Barcode")), ('language', _("Language")), ('type', _("Type")), ('status', _("Status")), ('cover', _("Cover")), ('score', _("Score")), ] self.cover_cells = [] self.fetching = False self.scrolled.connect(self.fetch_coverarts) def search(self, text): """Perform search using query provided by the user.""" self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_releases(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT) def show_similar_albums(self, cluster): """Perform search by using existing metadata information from the cluster as query.""" self.retry_params = Retry(self.show_similar_albums, cluster) self.cluster = cluster metadata = cluster.metadata query = { "artist": metadata["albumartist"], "release": metadata["album"], "tracks": str(len(cluster.files)) } # Generate query to be displayed to the user (in search box). # If advanced query syntax setting is enabled by user, display query in # advanced syntax style. Otherwise display only album title. if config.setting["use_adv_search_syntax"]: query_str = ' '.join(['%s:(%s)' % (item, escape_lucene_query(value)) for item, value in query.items() if value]) else: query_str = query["release"] query["limit"] = QUERY_LIMIT self.search_box_text(query_str) self.show_progress() self.tagger.mb_api.find_releases( self.handle_reply, **query) def retry(self): self.retry_params.function(self.retry_params.query) def handle_reply(self, document, http, error): if error: self.network_error(http, error) return try: releases = document['releases'] except (KeyError, TypeError): self.no_results_found() return del self.search_results[:] self.parse_releases(releases) self.display_results() self.fetch_coverarts() def fetch_coverarts(self): if self.fetching: return self.fetching = True for cell in self.cover_cells: self.fetch_coverart(cell) self.fetching = False def fetch_coverart(self, cell): """Queue cover art jsons from CAA server for each album in search results. """ if cell.fetched: return if not cell.is_visible(): return cell.fetched = True caa_path = "/release/%s" % cell.release["musicbrainz_albumid"] cell.fetch_task = self.tagger.webservice.get( CAA_HOST, CAA_PORT, caa_path, partial(self._caa_json_downloaded, cell) ) def _caa_json_downloaded(self, cover_cell, data, http, error): """Handle json reply from CAA server. If server replies without error, try to get small thumbnail of front coverart of the release. """ if not self.table: return cover_cell.fetch_task = None if error: cover_cell.not_found() return front = None try: for image in data["images"]: if image["front"]: front = image break if front: url = front["thumbnails"]["small"] coverartimage = CaaThumbnailCoverArtImage(url=url) cover_cell.fetch_task = self.tagger.webservice.download( coverartimage.host, coverartimage.port, coverartimage.path, partial(self._cover_downloaded, cover_cell) ) else: cover_cell.not_found() except (AttributeError, KeyError, TypeError): log.error("Error reading CAA response", exc_info=True) cover_cell.not_found() def _cover_downloaded(self, cover_cell, data, http, error): """Handle cover art query reply from CAA server. If server returns the cover image successfully, update the cover art cell of particular release. Args: row -- Album's row in results table """ if not self.table: return cover_cell.fetch_task = None if error: cover_cell.not_found() else: pixmap = QtGui.QPixmap() try: pixmap.loadFromData(data) cover_cell.set_pixmap(pixmap) except Exception as e: cover_cell.not_found() log.error(e) def fetch_cleanup(self): for cell in self.cover_cells: if cell.fetch_task is not None: log.debug("Removing cover art fetch task for %s", cell.release['musicbrainz_albumid']) self.tagger.webservice.remove_task(cell.fetch_task) def closeEvent(self, event): if self.cover_cells: self.fetch_cleanup() super().closeEvent(event) def parse_releases(self, releases): for node in releases: release = Metadata() release_to_metadata(node, release) release['score'] = node['score'] rg_node = node['release-group'] release_group_to_metadata(rg_node, release) if "media" in node: media = node['media'] release["format"] = media_formats_from_node(media) release["tracks"] = node['track-count'] countries = countries_from_node(node) if countries: release["country"] = ", ".join(countries) self.search_results.append(release) def display_results(self): self.prepare_table() self.cover_cells = [] for row, release in enumerate(self.search_results): self.table.insertRow(row) self.set_table_item(row, 'name', release, "album") self.set_table_item(row, 'artist', release, "albumartist") self.set_table_item(row, 'format', release, "format") self.set_table_item(row, 'tracks', release, "tracks", sort=BY_NUMBER) self.set_table_item(row, 'date', release, "date") self.set_table_item(row, 'country', release, "country") self.set_table_item(row, 'labels', release, "label") self.set_table_item(row, 'catnums', release, "catalognumber") self.set_table_item(row, 'barcode', release, "barcode", sort=BY_NUMBER) self.set_table_item(row, 'language', release, "~releaselanguage") self.set_table_item(row, 'type', release, "releasetype") self.set_table_item(row, 'status', release, "releasestatus") self.set_table_item(row, 'score', release, "score", sort=BY_NUMBER) self.cover_cells.append(CoverCell(self, release, row, 'cover', on_show=self.fetch_coverart)) self.show_table(sort_column='score') def accept_event(self, arg): self.load_selection(arg) def load_selection(self, row): release = self.search_results[row] self.tagger.get_release_group_by_id( release["musicbrainz_releasegroupid"]).loaded_albums.add( release["musicbrainz_albumid"]) album = self.tagger.load_album(release["musicbrainz_albumid"]) if self.cluster: files = self.tagger.get_files_from_objects([self.cluster]) self.tagger.move_files_to_album(files, release["musicbrainz_albumid"], album)
class ArtistSearchDialog(SearchDialog): dialog_header_state = "artistsearchdialog_header_state" options = [ config.Option("persist", dialog_header_state, QtCore.QByteArray()) ] def __init__(self, parent): super().__init__( parent, accept_button_title=_("Show in browser"), search_type="artist") self.setWindowTitle(_("Artist Search Dialog")) self.columns = [ ('name', _("Name")), ('type', _("Type")), ('gender', _("Gender")), ('area', _("Area")), ('begindate', _("Begin")), ('beginarea', _("Begin Area")), ('enddate', _("End")), ('endarea', _("End Area")), ('score', _("Score")), ] def search(self, text): self.retry_params = Retry(self.search, text) self.search_box_text(text) self.show_progress() self.tagger.mb_api.find_artists(self.handle_reply, query=text, search=True, limit=QUERY_LIMIT) def retry(self): self.retry_params.function(self.retry_params.query) def handle_reply(self, document, http, error): if error: self.network_error(http, error) return try: artists = document['artists'] except (KeyError, TypeError): self.no_results() return del self.search_results[:] self.parse_artists(artists) self.display_results() def parse_artists(self, artists): for node in artists: artist = Metadata() artist_to_metadata(node, artist) artist['score'] = node['score'] self.search_results.append(artist) def display_results(self): self.prepare_table() for row, artist in enumerate(self.search_results): self.table.insertRow(row) self.set_table_item(row, 'name', artist, "name") self.set_table_item(row, 'type', artist, "type") self.set_table_item(row, 'gender', artist, "gender") self.set_table_item(row, 'area', artist, "area") self.set_table_item(row, 'begindate', artist, "begindate") self.set_table_item(row, 'beginarea', artist, "beginarea") self.set_table_item(row, 'enddate', artist, "enddate") self.set_table_item(row, 'endarea', artist, "endarea") self.set_table_item(row, 'score', artist, "score", sort=BY_NUMBER) self.show_table(sort_column='score') def accept_event(self, row): self.load_in_browser(row) def load_in_browser(self, row): self.tagger.search(self.search_results[row]["musicbrainz_artistid"], "artist")