예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
 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)
예제 #4
0
파일: track.py 프로젝트: xprilion/picard
 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)
예제 #5
0
파일: album.py 프로젝트: Jormangeud/picard
    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)
예제 #6
0
파일: track.py 프로젝트: Jormangeud/picard
    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)
예제 #7
0
파일: artist.py 프로젝트: Jormangeud/picard
 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)
예제 #8
0
파일: track.py 프로젝트: Jormangeud/picard
 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)
예제 #9
0
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)
예제 #10
0
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)
예제 #11
0
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")
예제 #12
0
파일: track.py 프로젝트: Jormangeud/picard
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)
예제 #13
0
파일: album.py 프로젝트: Jormangeud/picard
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)
예제 #14
0
파일: artist.py 프로젝트: Jormangeud/picard
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")