Example #1
0
def main(localedir=None, autoupdate=True):
    # Some libs (ie. Phonon) require those to be set
    QtWidgets.QApplication.setApplicationName(PICARD_APP_NAME)
    QtWidgets.QApplication.setOrganizationName(PICARD_ORG_NAME)

    signal.signal(signal.SIGINT, signal.SIG_DFL)

    picard_args, unparsed_args = process_picard_args()
    if picard_args.version:
        return version()
    if picard_args.long_version:
        return longversion()

    tagger = Tagger(picard_args, unparsed_args, localedir, autoupdate)

    # Initialize Qt default translations
    translator = QtCore.QTranslator()
    locale = QtCore.QLocale()
    translation_path = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)
    log.debug("Looking for Qt locale %s in %s", locale.name(), translation_path)
    if translator.load(locale, "qtbase_", directory=translation_path):
        tagger.installTranslator(translator)
    else:
        log.warning('Error loading Qt locale %s', locale.name())

    tagger.startTimer(1000)
    sys.exit(tagger.run())
Example #2
0
    def on_remote_image_fetched(self, url, data, reply, error, fallback_data=None):
        if error:
            log.error("Failed loading remote image from %s: %s", url, reply.errorString())
            if fallback_data:
                self._load_fallback_data(url, fallback_data)
            return

        data = bytes(data)
        mime = reply.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
        # Some sites return a mime type with encoding like "image/jpeg; charset=UTF-8"
        mime = mime.split(';')[0]
        url_query = QtCore.QUrlQuery(url.query())
        # If mime indicates only binary data we can try to guess the real mime type
        if mime in ('application/octet-stream', 'binary/data'):
            mime = imageinfo.identify(data)[2]
        if mime in ('image/jpeg', 'image/png'):
            self.load_remote_image(url, mime, data)
        elif url_query.hasQueryItem("imgurl"):
            # This may be a google images result, try to get the URL which is encoded in the query
            url = QtCore.QUrl(url_query.queryItemValue("imgurl", QtCore.QUrl.FullyDecoded))
            self.fetch_remote_image(url)
        elif url_query.hasQueryItem("mediaurl"):
            # Bing uses mediaurl
            url = QtCore.QUrl(url_query.queryItemValue("mediaurl", QtCore.QUrl.FullyDecoded))
            self.fetch_remote_image(url)
        else:
            log.warning("Can't load remote image with MIME-Type %s", mime)
            if fallback_data:
                self._load_fallback_data(url, fallback_data)
Example #3
0
    def _coverart_downloaded(self, coverartimage, data, http, error):
        """Handle finished download, save it to metadata"""
        self.album._requests -= 1

        if error:
            self.album.error_append('Coverart error: %s' %
                                    (http.errorString()))
        elif len(data) < 1000:
            log.warning("Not enough data, skipping %s" % coverartimage)
        else:
            self._message(N_(
                "Cover art of type '%(type)s' downloaded for %(albumid)s from %(host)s"
            ), {
                'type': coverartimage.types_as_string(),
                'albumid': self.album.id,
                'host': coverartimage.host
            },
                          echo=None)
            try:
                self._set_metadata(coverartimage, data)
            except CoverArtImageIOError:
                # It doesn't make sense to store/download more images if we can't
                # save them in the temporary folder, abort.
                return

        self.next_in_queue()
Example #4
0
 def on_remote_image_fetched(self,
                             url,
                             data,
                             reply,
                             error,
                             fallback_data=None):
     data = bytes(data)
     mime = reply.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
     url_query = QtCore.QUrlQuery(url.query())
     if mime in ('image/jpeg', 'image/png'):
         self.load_remote_image(url, mime, data)
     elif url_query.hasQueryItem("imgurl"):
         # This may be a google images result, try to get the URL which is encoded in the query
         url = QtCore.QUrl(
             url_query.queryItemValue("imgurl", QtCore.QUrl.FullyDecoded))
         self.fetch_remote_image(url)
     else:
         log.warning("Can't load remote image with MIME-Type %s", mime)
         if fallback_data:
             # Tests for image format obtained from file-magic
             try:
                 mime = imageinfo.identify(fallback_data)[2]
             except imageinfo.IdentificationError as e:
                 log.error("Unable to identify dropped data format: %s" % e)
             else:
                 self.load_remote_image(url, mime, fallback_data)
                 log.debug("Trying the dropped %s data", mime)
Example #5
0
    def create_action_toolbar(self):
        if getattr(self, 'toolbar', None):
            self.toolbar.clear()
            self.removeToolBar(self.toolbar)
        self.toolbar = toolbar = QtGui.QToolBar(_(u"Actions"))
        self.insertToolBar(self.search_toolbar, self.toolbar)
        self.toolbar_toggle_action = self.toolbar.toggleViewAction()
        self.update_toolbar_style()
        toolbar.setObjectName("main_toolbar")

        def add_toolbar_action(action):
            toolbar.addAction(action)
            widget = toolbar.widgetForAction(action)
            widget.setFocusPolicy(QtCore.Qt.TabFocus)
            widget.setAttribute(QtCore.Qt.WA_MacShowFocusRect)

        for action in config.setting['toolbar_layout']:
            if action not in ('cd_lookup_action', 'separator'):
                try:
                    add_toolbar_action(getattr(self, action))
                except AttributeError:
                    log.warning('Warning: Unknown action name "%r" found in config. Ignored.', action)
            elif action == 'cd_lookup_action':
                add_toolbar_action(self.cd_lookup_action)
                drives = get_cdrom_drives()
                if len(drives) > 1:
                    self.cd_lookup_menu = QtGui.QMenu()
                    for drive in drives:
                        self.cd_lookup_menu.addAction(drive)
                    self.cd_lookup_menu.triggered.connect(self.tagger.lookup_cd)
                    button = toolbar.widgetForAction(self.cd_lookup_action)
                    button.setPopupMode(QtGui.QToolButton.MenuButtonPopup)
                    button.setMenu(self.cd_lookup_menu)
            elif action == 'separator':
                toolbar.addSeparator()
Example #6
0
def create_work_and_movement_from_title(work):
    """
    Attempts to parse work.title in the form "<Work>: <Number>. <Movement>",
    where <Number> is in Roman numerals.
    Sets the `is_movement` and `part_number` properties on `work` and creates
    a `parent` work if not already present.
    """
    title = work.title
    match = parse_work_name(title)
    if match:
        work.title = match.group('movement')
        work.is_movement = True
        try:
            number = number_to_int(match.group('movementnumber'))
        except ValueError as e:
            log.error(e)
            number = 0
        if not work.part_number:
            work.part_number = number
        elif work.part_number != number:
            log.warning(
                'Movement number mismatch for "%s": %s != %i' %
                (title, match.group('movementnumber'), work.part_number))
        if not work.parent:
            work.parent = Work(match.group('work'))
            work.parent.is_work = True
        elif work.parent.title != match.group('work'):
            log.warning('Movement work name mismatch for "%s": "%s" != "%s"' %
                        (title, match.group('work'), work.parent.title))
    return work
    def process_metadata(self, album, metadata, track, release):
        if not config.setting['happidev_apikey']:
            error = 'API key is missing, please provide a valid value'
            log.warning('{}: {}'.format(PLUGIN_NAME, error))
            return

        artist = metadata['artist']
        title = metadata['title']
        if not (artist and title):
            log.debug(
                '{}: both artist and title are required to obtain lyrics'.
                format(PLUGIN_NAME))
            return

        path = '/v1/music'
        queryargs = {
            'q': '"{}" "{}"'.format(artist, title),
            'lyrics': 'true',
            'type': 'track',
        }
        album._requests += 1
        log.debug('{}: GET {}?{}'.format(PLUGIN_NAME, quote(path),
                                         urlencode(queryargs)))
        self._request(album.tagger.webservice, path,
                      partial(self.process_search_response, album, metadata),
                      queryargs)
Example #8
0
    def extract_and_submit_acousticbrainz_features(self, objs):
        """Extract AcousticBrainz features and submit them."""
        if not self.ab_extractor.available():
            return

        for file in iter_files_from_objects(objs):
            # Skip unmatched files
            if not file.can_extract():
                log.warning(
                    "AcousticBrainz requires a MusicBrainz Recording ID, but file does not have it: %s"
                    % file.filename)
            # And process matched ones
            else:
                file.set_pending()

                # Check if file was either already processed or sent to the AcousticBrainz server
                if file.acousticbrainz_features_file:
                    results = (file.acousticbrainz_features_file, 0,
                               "Writing results")
                    ab_extractor_callback(self, file, results, False)
                elif file.acousticbrainz_is_duplicate:
                    results = (None, 0, "Duplicate")
                    ab_extractor_callback(self, file, results, False)
                else:
                    file.acousticbrainz_error = False
                    # Launch the acousticbrainz on a separate process
                    log.debug("Extracting AcousticBrainz features from %s" %
                              file.filename)
                    ab_feature_extraction(
                        self, file.metadata["musicbrainz_recordingid"],
                        file.filename,
                        partial(ab_extractor_callback, self, file))
    def process_search_response(self, album, metadata, response, reply, error):
        if self._handle_error(album, error, response):
            log.warning('{}: lyrics NOT found for track "{}" by {}'.format(
                PLUGIN_NAME, metadata['title'], metadata['artist']))
            return

        try:
            lyrics_url = response['result'][0]['api_lyrics']
            log.debug('{}: lyrics found for track "{}" by {} at {}'.format(
                PLUGIN_NAME, metadata['title'], metadata['artist'],
                lyrics_url))
            path = urlparse(lyrics_url).path

        except (TypeError, KeyError, ValueError):
            log.warn(
                '{}: failed parsing search response for "{}" by {}'.format(
                    PLUGIN_NAME, metadata['title'], metadata['artist']),
                exc_info=True)
            album._requests -= 1
            album._finalize_loading(None)
            return

        self._request(album.tagger.webservice,
                      path,
                      partial(self.process_lyrics_response, album, metadata),
                      important=True)
Example #10
0
def main(localedir=None, autoupdate=True):
    # Some libs (ie. Phonon) require those to be set
    QtWidgets.QApplication.setApplicationName(PICARD_APP_NAME)
    QtWidgets.QApplication.setOrganizationName(PICARD_ORG_NAME)

    signal.signal(signal.SIGINT, signal.SIG_DFL)

    picard_args, unparsed_args = process_picard_args()
    if picard_args.version:
        return version()
    if picard_args.long_version:
        return longversion()

    tagger = Tagger(picard_args, unparsed_args, localedir, autoupdate)

    # Initialize Qt default translations
    translator = QtCore.QTranslator()
    locale = QtCore.QLocale()
    translation_path = QtCore.QLibraryInfo.location(
        QtCore.QLibraryInfo.TranslationsPath)
    log.debug("Looking for Qt locale %s in %s", locale.name(),
              translation_path)
    if translator.load(locale, "qtbase_", directory=translation_path):
        tagger.installTranslator(translator)
    else:
        log.warning('Error loading Qt locale %s', locale.name())

    tagger.startTimer(1000)
    sys.exit(tagger.run())
Example #11
0
 def load_plugindir(self, plugindir):
     plugindir = os.path.normpath(plugindir)
     if not os.path.isdir(plugindir):
         log.warning("Plugin directory %r doesn't exist", plugindir)
         return
     #  first, handle eventual plugin updates
     for updatepath in [os.path.join(plugindir, file) for file in os.listdir(plugindir) if file.endswith(".update")]:
         path = os.path.splitext(updatepath)[0]
         name = is_zip(path)
         if not name:
             name = _plugin_name_from_path(path)
         if name:
             self.remove_plugin(name)
             os.rename(updatepath, path)
             log.debug("Updating plugin %r (%r))", name, path)
         else:
             log.error("Cannot get plugin name from %r", updatepath)
     # now load found plugins
     names = set()
     for path in [os.path.join(plugindir, file) for file in os.listdir(plugindir)]:
         name = is_zip(path)
         if not name:
             name = _plugin_name_from_path(path)
         if name:
             names.add(name)
     log.debug("Looking for plugins in directory %r, %d names found", plugindir, len(names))
     for name in sorted(names):
         self.load_plugin(name, plugindir)
Example #12
0
 def load_remote_image(self, url, mime, data):
     try:
         coverartimage = CoverArtImage(
             url=url.toString(),
             data=data
         )
     except CoverArtImageError as e:
         log.warning("Can't load image: %s" % unicode(e))
         return
     pixmap = QtGui.QPixmap()
     pixmap.loadFromData(data)
     self.__set_data([mime, data], pixmap=pixmap)
     if isinstance(self.item, Album):
         album = self.item
         album.metadata.append_image(coverartimage)
         for track in album.tracks:
             track.metadata.append_image(coverartimage)
         for file in album.iterfiles():
             file.metadata.append_image(coverartimage)
     elif isinstance(self.item, Track):
         track = self.item
         track.metadata.append_image(coverartimage)
         for file in track.iterfiles():
             file.metadata.append_image(coverartimage)
     elif isinstance(self.item, File):
         file = self.item
         file.metadata.append_image(coverartimage)
Example #13
0
 def load_remote_image(self, url, mime, data):
     try:
         coverartimage = CoverArtImage(
             url=url.toString(),
             data=data
         )
     except CoverArtImageError as e:
         log.warning("Can't load image: %s" % unicode(e))
         return
     if isinstance(self.item, Album):
         album = self.item
         album.metadata.append_image(coverartimage)
         for track in album.tracks:
             track.metadata.append_image(coverartimage)
         for file in album.iterfiles():
             file.metadata.append_image(coverartimage)
             file.update()
     elif isinstance(self.item, Track):
         track = self.item
         track.metadata.append_image(coverartimage)
         for file in track.iterfiles():
             file.metadata.append_image(coverartimage)
             file.update()
     elif isinstance(self.item, File):
         file = self.item
         file.metadata.append_image(coverartimage)
         file.update()
     self.cover_art.set_metadata(self.item.metadata)
     self.show()
Example #14
0
 def load_plugindir(self, plugindir):
     plugindir = os.path.normpath(plugindir)
     if not os.path.isdir(plugindir):
         log.warning("Plugin directory %r doesn't exist", plugindir)
         return
     # first, handle eventual plugin updates
     for updatepath in [
             os.path.join(plugindir, file) for file in os.listdir(plugindir)
             if file.endswith('.update')
     ]:
         path = os.path.splitext(updatepath)[0]
         name = is_zip(path)
         if not name:
             name = _plugin_name_from_path(path)
         if name:
             self.remove_plugin(name)
             os.rename(updatepath, path)
             log.debug('Updating plugin %r (%r))', name, path)
         else:
             log.error('Cannot get plugin name from %r', updatepath)
     # now load found plugins
     names = set()
     for path in [
             os.path.join(plugindir, file) for file in os.listdir(plugindir)
     ]:
         name = is_zip(path)
         if not name:
             name = _plugin_name_from_path(path)
         if name:
             names.add(name)
     log.debug("Looking for plugins in directory %r, %d names found",
               plugindir, len(names))
     for name in sorted(names):
         self.load_plugin(name, plugindir)
Example #15
0
def normpath(path):
    try:
        path = os.path.realpath(path)
    except OSError as why:
        # realpath can fail if path does not exist or is not accessible
        log.warning('Failed getting realpath for "%s": %s', path, why)
    return os.path.normpath(path)
Example #16
0
    def create_action_toolbar(self):
        if self.toolbar:
            self.toolbar.clear()
            self.removeToolBar(self.toolbar)
        self.toolbar = toolbar = QtWidgets.QToolBar(_("Actions"))
        self.insertToolBar(self.search_toolbar, self.toolbar)
        self.update_toolbar_style()
        toolbar.setObjectName("main_toolbar")

        def add_toolbar_action(action):
            toolbar.addAction(action)
            widget = toolbar.widgetForAction(action)
            widget.setFocusPolicy(QtCore.Qt.TabFocus)
            widget.setAttribute(QtCore.Qt.WA_MacShowFocusRect)

        for action in config.setting['toolbar_layout']:
            if action == 'cd_lookup_action':
                add_toolbar_action(self.cd_lookup_action)
                if len(self.cd_lookup_menu.actions()) > 1:
                    button = toolbar.widgetForAction(self.cd_lookup_action)
                    button.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)
                    button.setMenu(self.cd_lookup_menu)
            elif action == 'separator':
                toolbar.addSeparator()
            else:
                try:
                    add_toolbar_action(getattr(self, action))
                except AttributeError:
                    log.warning('Warning: Unknown action name "%r" found in config. Ignored.', action)
        self.show_toolbar()
Example #17
0
    def _coverart_downloaded(self, coverartimage, data, http, error):
        """Handle finished download, save it to metadata"""
        self.album._requests -= 1

        if error:
            self.album.error_append(u'Coverart error: %s' % (unicode(http.errorString())))
        elif len(data) < 1000:
            log.warning("Not enough data, skipping %s" % coverartimage)
        else:
            self._message(
                N_("Cover art of type '%(type)s' downloaded for %(albumid)s from %(host)s"),
                {
                    'type': coverartimage.types_as_string(),
                    'albumid': self.album.id,
                    'host': coverartimage.host
                },
                echo=None
            )
            try:
                self._set_metadata(coverartimage, data)
            except CoverArtImageIOError:
                # It doesn't make sense to store/download more images if we can't
                # save them in the temporary folder, abort.
                return

        self.next_in_queue()
Example #18
0
    def create_action_toolbar(self):
        if getattr(self, 'toolbar', None):
            self.toolbar.clear()
            self.removeToolBar(self.toolbar)
        self.toolbar = toolbar = QtWidgets.QToolBar(_("Actions"))
        self.insertToolBar(self.search_toolbar, self.toolbar)
        self.update_toolbar_style()
        toolbar.setObjectName("main_toolbar")

        def add_toolbar_action(action):
            toolbar.addAction(action)
            widget = toolbar.widgetForAction(action)
            widget.setFocusPolicy(QtCore.Qt.TabFocus)
            widget.setAttribute(QtCore.Qt.WA_MacShowFocusRect)

        for action in config.setting['toolbar_layout']:
            if action == 'cd_lookup_action':
                add_toolbar_action(self.cd_lookup_action)
                if len(self.cd_lookup_menu.actions()) > 1:
                    button = toolbar.widgetForAction(self.cd_lookup_action)
                    button.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)
                    button.setMenu(self.cd_lookup_menu)
            elif action == 'separator':
                toolbar.addSeparator()
            else:
                try:
                    add_toolbar_action(getattr(self, action))
                except AttributeError:
                    log.warning(
                        'Warning: Unknown action name "%r" found in config. Ignored.',
                        action)
        self.show_toolbar()
Example #19
0
    def on_remote_image_fetched(self, url, data, reply, error, fallback_data=None):
        if error:
            log.error("Failed loading remote image from %s: %s", url, reply.errorString())
            if fallback_data:
                self._load_fallback_data(url, fallback_data)
            return

        data = bytes(data)
        mime = reply.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
        # Some sites return a mime type with encoding like "image/jpeg; charset=UTF-8"
        mime = mime.split(';')[0]
        url_query = QtCore.QUrlQuery(url.query())
        log.debug('Fetched remote image with MIME-Type %s from %s', mime, url.toString())
        # If mime indicates only binary data we can try to guess the real mime type
        if (mime in ('application/octet-stream', 'binary/data') or mime.startswith('image/')
              or imageinfo.supports_mime_type(mime)):
            try:
                self._try_load_remote_image(url, data)
                return
            except CoverArtImageError:
                pass
        if url_query.hasQueryItem("imgurl"):
            # This may be a google images result, try to get the URL which is encoded in the query
            url = QtCore.QUrl(url_query.queryItemValue("imgurl", QtCore.QUrl.FullyDecoded))
            log.debug('Possible Google images result, trying to fetch imgurl=%s', url.toString())
            self.fetch_remote_image(url)
        elif url_query.hasQueryItem("mediaurl"):
            # Bing uses mediaurl
            url = QtCore.QUrl(url_query.queryItemValue("mediaurl", QtCore.QUrl.FullyDecoded))
            log.debug('Possible Bing images result, trying to fetch imgurl=%s', url.toString())
            self.fetch_remote_image(url)
        else:
            log.warning("Can't load remote image with MIME-Type %s", mime)
            if fallback_data:
                self._load_fallback_data(url, fallback_data)
Example #20
0
 def load_remote_image(self, url, mime, data):
     try:
         coverartimage = CoverArtImage(url=url.toString(), data=data)
     except CoverArtImageError as e:
         log.warning("Can't load image: %s" % unicode(e))
         return
     pixmap = QtGui.QPixmap()
     pixmap.loadFromData(data)
     self.__set_data([mime, data], pixmap=pixmap)
     if isinstance(self.item, Album):
         album = self.item
         album.metadata.append_image(coverartimage)
         for track in album.tracks:
             track.metadata.append_image(coverartimage)
         for file in album.iterfiles():
             file.metadata.append_image(coverartimage)
     elif isinstance(self.item, Track):
         track = self.item
         track.metadata.append_image(coverartimage)
         for file in track.iterfiles():
             file.metadata.append_image(coverartimage)
     elif isinstance(self.item, File):
         file = self.item
         file.metadata.append_image(coverartimage)
     self.currentImage = len(self.metadata.images) - 1
     self.__update_image_count()
Example #21
0
    def load_remote_image(self, url, mime, data):
        try:
            coverartimage = CoverArtImage(
                url=url.toString(),
                types=['front'],
                data=data
            )
        except CoverArtImageError as e:
            log.warning("Can't load image: %s" % e)
            return

        if config.setting["load_image_behavior"] == 'replace':
            set_image = set_image_replace
            debug_info = "Replacing with dropped %r in %r"
        else:
            set_image = set_image_append
            debug_info = "Appending dropped %r to %r"

        update = True
        if isinstance(self.item, Album):
            album = self.item
            album.enable_update_metadata_images(False)
            set_image(album, coverartimage)
            for track in album.tracks:
                set_image(track, coverartimage)
                track.metadata_images_changed.emit()
            for file in album.iterfiles():
                set_image(file, coverartimage)
                file.metadata_images_changed.emit()
                file.update()
            album.enable_update_metadata_images(True)
            album.update_metadata_images()
            album.update(False)
        elif isinstance(self.item, Track):
            track = self.item
            track.album.enable_update_metadata_images(False)
            set_image(track, coverartimage)
            track.metadata_images_changed.emit()
            for file in track.iterfiles():
                set_image(file, coverartimage)
                file.metadata_images_changed.emit()
                file.update()
            track.album.enable_update_metadata_images(True)
            track.album.update_metadata_images()
            track.album.update(False)
        elif isinstance(self.item, File):
            file = self.item
            set_image(file, coverartimage)
            file.metadata_images_changed.emit()
            file.update()
        else:
            debug_info = "Dropping %r to %r is not handled"
            update = False

        log.debug(debug_info, coverartimage, self.item)

        if update:
            self.cover_art.set_metadata(self.item.metadata)
            self.show()
Example #22
0
 def is_dark_theme(self):
     dark_theme = False
     try:
         with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize") as key:
             dark_theme = winreg.QueryValueEx(key, "AppsUseLightTheme")[0] == 0
     except OSError:
         log.warning('Failed reading AppsUseLightTheme from registry')
     return dark_theme
Example #23
0
def _macos_find_root_volume():
    try:
        for entry in os.scandir('/Volumes/'):
            if entry.is_symlink() and os.path.realpath(entry.path) == '/':
                return entry.path
    except OSError:
        log.warning('Could not detect macOS boot volume', exc_info=True)
    return None
Example #24
0
 def load_mbid(self, type, mbid):
     self.bring_tagger_front()
     if type == 'album':
         self.load_album(mbid)
     elif type == 'nat':
         self.load_nat(mbid)
     else:
         log.warning('Unknown type to load: %s', type)
Example #25
0
    def load_remote_image(self, url, mime, data):
        try:
            coverartimage = CoverArtImage(url=url.toString(),
                                          types=['front'],
                                          data=data)
        except CoverArtImageError as e:
            log.warning("Can't load image: %s" % e)
            return

        if config.setting["load_image_behavior"] == 'replace':
            set_image = set_image_replace
            debug_info = "Replacing with dropped %r in %r"
        else:
            set_image = set_image_append
            debug_info = "Appending dropped %r to %r"

        if isinstance(self.item, Album):
            album = self.item
            album.enable_update_metadata_images(False)
            set_image(album, coverartimage)
            for track in album.tracks:
                track.enable_update_metadata_images(False)
                set_image(track, coverartimage)
            for file in album.iterfiles():
                set_image(file, coverartimage)
                file.update(signal=False)
            for track in album.tracks:
                track.enable_update_metadata_images(True)
            album.enable_update_metadata_images(True)
            album.update(update_tracks=False)
        elif isinstance(self.item, FileListItem):
            parents = set()
            filelist = self.item
            filelist.enable_update_metadata_images(False)
            set_image(filelist, coverartimage)
            for file in filelist.iterfiles():
                for parent in iter_file_parents(file):
                    parent.enable_update_metadata_images(False)
                    parents.add(parent)
                set_image(file, coverartimage)
                file.update(signal=False)
            for parent in parents:
                set_image(parent, coverartimage)
                parent.enable_update_metadata_images(True)
                if isinstance(parent, Album):
                    parent.update(update_tracks=False)
                else:
                    parent.update()
            filelist.enable_update_metadata_images(True)
            filelist.update()
        elif isinstance(self.item, File):
            file = self.item
            set_image(file, coverartimage)
            file.update()
        else:
            debug_info = "Dropping %r to %r is not handled"

        log.debug(debug_info, coverartimage, self.item)
Example #26
0
File: asf.py Project: ruipin/picard
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        config = get_config()
        self.__casemap = {}
        file = ASF(encode_filename(filename))
        metadata = Metadata()
        for name, values in file.tags.items():
            if name == 'WM/Picture':
                for image in values:
                    try:
                        (mime, data, image_type,
                         description) = unpack_image(image.value)
                    except ValueError as e:
                        log.warning('Cannot unpack image from %r: %s',
                                    filename, e)
                        continue
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(image_type),
                            comment=description,
                            support_types=True,
                            data=data,
                            id3_type=image_type,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.images.append(coverartimage)

                continue
            elif name == 'WM/SharedUserRating':
                # Rating in WMA ranges from 0 to 99, normalize this to the range 0 to 5
                values[0] = int(
                    round(
                        int(str(values[0])) / 99.0 *
                        (config.setting['rating_steps'] - 1)))
            elif name == 'WM/PartOfSet':
                disc = str(values[0]).split("/")
                if len(disc) > 1:
                    metadata["totaldiscs"] = disc[1]
                    values[0] = disc[0]
            name_lower = name.lower()
            if name in self.__RTRANS:
                name = self.__RTRANS[name]
            elif name_lower in self.__RTRANS_CI:
                orig_name = name
                name = self.__RTRANS_CI[name_lower]
                self.__casemap[name] = orig_name
            else:
                continue
            values = [str(value) for value in values if value]
            if values:
                metadata[name] = values
        self._info(metadata, file)
        return metadata
Example #27
0
 def queue_images(self):
     # this method has to return CoverArtProvider.FINISHED or
     # CoverArtProvider.WAIT
     old = getattr(self, 'queue_downloads') #compat with old plugins
     if callable(old):
         log.warning('CoverArtProvider: queue_downloads() was replaced by queue_images()')
         return old()
     else:
         raise NotImplementedError
 def handle_cached_toptags(self, tagtype, query):
     """
     Copy toptags from module-global cache to local toptags list.
     """
     toptags = CACHE.get(query, None)
     if toptags is not None:
         self.toptags[tagtype].extend(toptags)  # noqa
     else:
         log.warning('cache error: %s, %s', tagtype, query)
Example #29
0
 def on_remote_image_fetched(self, data, reply, error):
     mime = reply.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
     if mime in ('image/jpeg', 'image/png'):
         self.load_remote_image(mime, data)
     elif reply.url().hasQueryItem("imgurl"):
         # This may be a google images result, try to get the URL which is encoded in the query
         url = QtCore.QUrl(reply.url().queryItemValue("imgurl"))
         self.fetch_remote_image(url)
     else:
         log.warning("Can't load image with MIME-Type %s", mime)
Example #30
0
 def on_remote_image_fetched(self, url, data, reply, error):
     mime = reply.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
     if mime in ('image/jpeg', 'image/png'):
         self.load_remote_image(url, mime, data)
     elif reply.url().hasQueryItem("imgurl"):
         # This may be a google images result, try to get the URL which is encoded in the query
         url = QtCore.QUrl(reply.url().queryItemValue("imgurl"))
         self.fetch_remote_image(url)
     else:
         log.warning("Can't load image with MIME-Type %s", mime)
Example #31
0
 def get_accent_color(self):
     accent_color = None
     try:
         with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\DWM") as key:
             accent_color_dword = winreg.QueryValueEx(key, "ColorizationColor")[0]
             accent_color_hex = '#{:06x}'.format(accent_color_dword & 0xffffff)
             accent_color = QtGui.QColor(accent_color_hex)
     except OSError:
         log.warning('Failed reading ColorizationColor from registry')
     return accent_color
Example #32
0
    def _display_artwork(self, images, col):
        """Draw artwork in corresponding cell if image type matches type in Type column.

        Arguments:
        images -- The images to be drawn.
        col -- Column in which images are to be drawn. Can be _new_cover_col or _existing_cover_col.
        """
        row = 0
        row_count = self.artwork_table.rowCount()
        for image in images:
            while row != row_count:
                image_type = self.artwork_table.item(
                    row, self.artwork_table._type_col)
                if image_type and image_type.data(
                        QtCore.Qt.UserRole) == image.types_as_string():
                    break
                row += 1
            if row == row_count:
                continue
            data = None
            try:
                if image.thumbnail:
                    try:
                        data = image.thumbnail.data
                    except CoverArtImageIOError as e:
                        log.warning(unicode(e))
                        pass
                else:
                    data = image.data
            except CoverArtImageIOError:
                log.error(traceback.format_exc())
                continue
            item = QtWidgets.QTableWidgetItem()
            item.setData(QtCore.Qt.UserRole, image)
            pixmap = QtGui.QPixmap()
            if data is not None:
                pixmap.loadFromData(data)
                item.setToolTip(
                    _("Double-click to open in external viewer\n"
                      "Temporary file: %s\n"
                      "Source: %s") % (image.tempfile_filename, image.source))
            infos = []
            if image.comment:
                infos.append(image.comment)
            infos.append(u"%s (%s)" % (bytes2human.decimal(
                image.datalength), bytes2human.binary(image.datalength)))
            if image.width and image.height:
                infos.append(u"%d x %d" % (image.width, image.height))
            infos.append(image.mimetype)

            img_wgt = self.artwork_table.get_coverart_widget(
                pixmap, "\n".join(infos))
            self.artwork_table.setCellWidget(row, col, img_wgt)
            self.artwork_table.setItem(row, col, item)
            row += 1
Example #33
0
 def _save_and_rename(self, old_filename, metadata):
     """Save the metadata."""
     # Check that file has not been removed since thread was queued
     # Also don't save if we are stopping.
     if self.state == File.REMOVED:
         log.debug("File not saved because it was removed: %r",
                   self.filename)
         return None
     if self.tagger.stopping:
         log.debug("File not saved because %s is stopping: %r",
                   PICARD_APP_NAME, self.filename)
         return None
     new_filename = old_filename
     if not config.setting["dont_write_tags"]:
         save = partial(self._save, old_filename, metadata)
         if config.setting["preserve_timestamps"]:
             try:
                 self._preserve_times(old_filename, save)
             except self.PreserveTimesStatError as why:
                 log.warning(why)
                 # we didn't save the file yet, bail out
                 return None
             except self.FilePreserveTimesUtimeError as why:
                 log.warning(why)
         else:
             save()
     # Rename files
     if config.setting["rename_files"] or config.setting["move_files"]:
         new_filename = self._rename(old_filename, metadata)
     # Move extra files (images, playlists, etc.)
     if config.setting["move_files"] and config.setting[
             "move_additional_files"]:
         self._move_additional_files(old_filename, new_filename)
     # Delete empty directories
     if config.setting["delete_empty_dirs"]:
         dirname = os.path.dirname(old_filename)
         try:
             self._rmdir(dirname)
             head, tail = os.path.split(dirname)
             if not tail:
                 head, tail = os.path.split(head)
             while head and tail:
                 try:
                     self._rmdir(head)
                 except BaseException:
                     break
                 head, tail = os.path.split(head)
         except EnvironmentError:
             pass
     # Save cover art images
     if config.setting["save_images_to_files"]:
         self._save_images(os.path.dirname(new_filename), metadata)
     return new_filename
Example #34
0
    def _display_artwork(self, images, col):
        """Draw artwork in corresponding cell if image type matches type in Type column.

        Arguments:
        images -- The images to be drawn.
        col -- Column in which images are to be drawn. Can be _new_cover_col or _existing_cover_col.
        """
        row = 0
        row_count = self.artwork_table.rowCount()
        for image in images:
            while row != row_count:
                image_type = self.artwork_table.item(row, self.artwork_table._type_col)
                if image_type and image_type.data(QtCore.Qt.UserRole) == image.types_as_string():
                    break
                row += 1
            if row == row_count:
                continue
            data = None
            try:
                if image.thumbnail:
                    try:
                        data = image.thumbnail.data
                    except CoverArtImageIOError as e:
                        log.warning(e)
                        pass
                else:
                    data = image.data
            except CoverArtImageIOError:
                log.error(traceback.format_exc())
                continue
            item = QtWidgets.QTableWidgetItem()
            item.setData(QtCore.Qt.UserRole, image)
            pixmap = QtGui.QPixmap()
            if data is not None:
                pixmap.loadFromData(data)
                item.setToolTip(
                    _("Double-click to open in external viewer\n"
                      "Temporary file: %s\n"
                      "Source: %s") % (image.tempfile_filename, image.source))
            infos = []
            if image.comment:
                infos.append(image.comment)
            infos.append("%s (%s)" %
                         (bytes2human.decimal(image.datalength),
                          bytes2human.binary(image.datalength)))
            if image.width and image.height:
                infos.append("%d x %d" % (image.width, image.height))
            infos.append(image.mimetype)

            img_wgt = self.artwork_table.get_coverart_widget(pixmap, "\n".join(infos))
            self.artwork_table.setCellWidget(row, col, img_wgt)
            self.artwork_table.setItem(row, col, item)
            row += 1
Example #35
0
    def load_plugin(self, name, plugindir):
        try:
            info = imp.find_module(name, [plugindir])
        except ImportError:
            log.error("Failed loading plugin %r", name)
            return None

        plugin = None
        try:
            index = None
            for i, p in enumerate(self.plugins):
                if name == p.module_name:
                    log.warning("Module %r conflict: unregistering previously" \
                              " loaded %r version %s from %r",
                              p.module_name,
                              p.name,
                              p.version,
                              p.file)
                    _unregister_module_extensions(name)
                    index = i
                    break
            plugin_module = imp.load_module(_PLUGIN_MODULE_PREFIX + name,
                                            *info)
            plugin = PluginWrapper(plugin_module, plugindir, file=info[1])
            versions = [
                version_from_string(v) for v in list(plugin.api_versions)
            ]
            compatible_versions = list(set(versions) & self._api_versions)
            if compatible_versions:
                log.debug(
                    "Loading plugin %r version %s, compatible with API: %s",
                    plugin.name, plugin.version, ", ".join([
                        version_to_string(v, short=True)
                        for v in sorted(compatible_versions)
                    ]))
                plugin.compatible = True
                setattr(picard.plugins, name, plugin_module)
                if index:
                    self.plugins[index] = plugin
                else:
                    self.plugins.append(plugin)
            else:
                log.warning("Plugin '%s' from '%s' is not compatible"
                            " with this version of Picard." %
                            (plugin.name, plugin.file))
        except VersionError as e:
            log.error("Plugin %r has an invalid API version string : %s", name,
                      e)
        except:
            log.error("Plugin %r : %s", name, traceback.format_exc())
        if info[0] is not None:
            info[0].close()
        return plugin
Example #36
0
    def _coverart_downloaded(self, coverartimage, data, http, error):
        """Handle finished download, save it to metadata"""
        self.album._requests -= 1

        if error:
            self.album.error_append(u'Coverart error: %s' % (unicode(http.errorString())))
        elif len(data) < 1000:
            log.warning("Not enough data, skipping %s" % coverartimage)
        else:
            self._message(
                N_("Cover art of type '%(type)s' downloaded for %(albumid)s from %(host)s"),
                {
                    'type': coverartimage.types_as_string(),
                    'albumid': self.album.id,
                    'host': coverartimage.host
                },
                echo=None
            )
            try:
                coverartimage.set_data(data)
                if coverartimage.can_be_saved_to_metadata:
                    log.debug("Cover art image downloaded: %r [%s]" %
                        (
                            coverartimage,
                            coverartimage.imageinfo_as_string()
                        )
                    )
                    self.metadata.append_image(coverartimage)
                    for track in self.album._new_tracks:
                        track.metadata.append_image(coverartimage)
                    # If the image already was a front image,
                    # there might still be some other non-CAA front
                    # images in the queue - ignore them.
                    if not self.front_image_found:
                        self.front_image_found = coverartimage.is_front_image()
                else:
                    log.debug("Thumbnail for cover art image downloaded: %r [%s]" %
                        (
                            coverartimage,
                            coverartimage.imageinfo_as_string()
                        )
                    )
            except CoverArtImageIOError as e:
                self.album.error_append(unicode(e))
                self.album._finalize_loading(error=True)
                # It doesn't make sense to store/download more images if we can't
                # save them in the temporary folder, abort.
                return
            except CoverArtImageIdentificationError as e:
                self.album.error_append(unicode(e))

        self.download_next_in_queue()
Example #37
0
    def barcode_process_metadata(self, barcode, response):

        # Check whether we have a concealed 404 and get the homepage
        if "<title>Contents - tango.info</title>" in response:
            log.debug("%s: No album with barcode %s on tango.info",
                      PLUGIN_NAME, barcode)
            return

        table = table_regex.search(response)
        if not table:
            log.warning("%s: No table found on page for barcode %s on tango.info",
                        PLUGIN_NAME, barcode)
            return

        albuminfo = {}
        trcontent = [match.groups()[0] for match in trs.finditer(table)]

        for tr in trcontent:
            trackinfo = [trmatch.groups()[0] for trmatch in tds.finditer(tr)]
            if not trackinfo: # check if list is empty, e.g. contains a <th>
                continue

            # Get genre
            if trackinfo[3] and not trackinfo[3] == "-":
                genre = unicode(
                            re.split('<|>', trackinfo[3])[2].title(), 'utf8'
                        )
            else:
                genre = False
            # Get date
            if trackinfo[6] and not trackinfo[6] == "-":
                date = unicode(re.split('<|>', trackinfo[6])[2], 'utf8')
            else:
                date = False
            # Get singers
            if trackinfo[5] == "-" or not trackinfo[5]:
                vocal = False
            elif trackinfo[5]:
                # Catch and strip <a> tags
                vocal = unicode(re.sub("<[^>]*>", "", trackinfo[5]), 'utf8')
            else:
                vocal = False

            # expected format in HTML: <a href="/002390...">...
            tint = trackinfo[8].split("\"")[1][1:]
            albuminfo[tint] = {
                                'genre': genre,
                                'date': date,
                                'vocal': vocal
                              }

        return albuminfo
Example #38
0
 def _save_and_rename(self, old_filename, metadata):
     """Save the metadata."""
     # Check that file has not been removed since thread was queued
     # Also don't save if we are stopping.
     if self.state == File.REMOVED:
         log.debug("File not saved because it was removed: %r",
                   self.filename)
         return None
     if self.tagger.stopping:
         log.debug("File not saved because %s is stopping: %r",
                   PICARD_APP_NAME, self.filename)
         return None
     new_filename = old_filename
     if not config.setting["dont_write_tags"]:
         encoded_old_filename = encode_filename(old_filename)
         info = os.stat(encoded_old_filename)
         self._save(old_filename, metadata)
         if config.setting["preserve_timestamps"]:
             try:
                 os.utime(encoded_old_filename,
                          (info.st_atime, info.st_mtime))
             except OSError:
                 log.warning("Couldn't preserve timestamp for %r",
                             old_filename)
     # Rename files
     if config.setting["rename_files"] or config.setting["move_files"]:
         new_filename = self._rename(old_filename, metadata)
     # Move extra files (images, playlists, etc.)
     if config.setting["move_files"] and config.setting[
             "move_additional_files"]:
         self._move_additional_files(old_filename, new_filename)
     # Delete empty directories
     if config.setting["delete_empty_dirs"]:
         dirname = encode_filename(os.path.dirname(old_filename))
         try:
             self._rmdir(dirname)
             head, tail = os.path.split(dirname)
             if not tail:
                 head, tail = os.path.split(head)
             while head and tail:
                 try:
                     self._rmdir(head)
                 except:
                     break
                 head, tail = os.path.split(head)
         except EnvironmentError:
             pass
     # Save cover art images
     if config.setting["save_images_to_files"]:
         self._save_images(os.path.dirname(new_filename), metadata)
     return new_filename
Example #39
0
 def _save_and_rename(self, old_filename, metadata):
     """Save the metadata."""
     # Check that file has not been removed since thread was queued
     # Also don't save if we are stopping.
     if self.state == File.REMOVED:
         log.debug("File not saved because it was removed: %r", self.filename)
         return None
     if self.tagger.stopping:
         log.debug("File not saved because %s is stopping: %r", PICARD_APP_NAME, self.filename)
         return None
     new_filename = old_filename
     if not config.setting["dont_write_tags"]:
         save = partial(self._save, old_filename, metadata)
         if config.setting["preserve_timestamps"]:
             try:
                 self._preserve_times(old_filename, save)
             except self.PreserveTimesStatError as why:
                 log.warning(why)
                 # we didn't save the file yet, bail out
                 return None
             except self.FilePreserveTimesUtimeError as why:
                 log.warning(why)
         else:
             save()
     # Rename files
     if config.setting["rename_files"] or config.setting["move_files"]:
         new_filename = self._rename(old_filename, metadata)
     # Move extra files (images, playlists, etc.)
     if config.setting["move_files"] and config.setting["move_additional_files"]:
         self._move_additional_files(old_filename, new_filename)
     # Delete empty directories
     if config.setting["delete_empty_dirs"]:
         dirname = os.path.dirname(old_filename)
         try:
             self._rmdir(dirname)
             head, tail = os.path.split(dirname)
             if not tail:
                 head, tail = os.path.split(head)
             while head and tail:
                 try:
                     self._rmdir(head)
                 except BaseException:
                     break
                 head, tail = os.path.split(head)
         except EnvironmentError:
             pass
     # Save cover art images
     if config.setting["save_images_to_files"]:
         self._save_images(os.path.dirname(new_filename), metadata)
     return new_filename
Example #40
0
def check_io_encoding():
    if _io_encoding == "ANSI_X3.4-1968":
        log.warning("""
System locale charset is ANSI_X3.4-1968
Your system's locale charset (i.e. the charset used to encode filenames)
is set to ANSI_X3.4-1968. It is highly unlikely that this has been done
intentionally. Most likely the locale is not set at all. An invalid setting
will result in problems when creating data projects.
To properly set the locale charset make sure the LC_* environment variables
are set. Normally the distribution setup tools take care of this.

Translation: Picard will have problems with non-english characters
               in filenames until you change your charset.
""")
Example #41
0
    def _coverart_downloaded(self, coverartimage, data, http, error):
        """Handle finished download, save it to metadata"""
        self.album._requests -= 1

        if error:
            self._coverart_http_error(http)
        elif len(data) < 1000:
            log.warning("Not enough data, skipping %s" % coverartimage)
        else:
            self._message(
                N_("Cover art of type '%(type)s' downloaded for %(albumid)s from %(host)s"),
                {
                    'type': ','.join(coverartimage.types),
                    'albumid': self.album.id,
                    'host': coverartimage.host
                }
            )
            mime = mimetype.get_from_data(data, default="image/jpeg")

            try:
                self.metadata.make_and_add_image(
                    mime,
                    data,
                    types=coverartimage.types,
                    comment=coverartimage.comment,
                    is_front=coverartimage.is_front
                )
                for track in self.album._new_tracks:
                    track.metadata.make_and_add_image(
                        mime,
                        data,
                        types=coverartimage.types,
                        comment=coverartimage.comment,
                        is_front=coverartimage.is_front
                    )
                # If the image already was a front image,
                # there might still be some other non-CAA front
                # images in the queue - ignore them.
                if not self.front_image_found:
                    self.front_image_found = coverartimage.is_front_image()

            except (IOError, OSError) as e:
                self.album.error_append(e.message)
                self.album._finalize_loading(error=True)
                # It doesn't make sense to store/download more images if we can't
                # save them in the temporary folder, abort.
                return

        self._download_next_in_queue()
Example #42
0
    def load_plugin(self, name, plugindir):
        try:
            info = imp.find_module(name, [plugindir])
        except ImportError:
            log.error("Failed loading plugin %r", name)
            return None

        plugin = None
        try:
            index = None
            for i, p in enumerate(self.plugins):
                if name == p.module_name:
                    log.warning("Module %r conflict: unregistering previously" \
                              " loaded %r version %s from %r",
                              p.module_name,
                              p.name,
                              p.version,
                              p.file)
                    _unregister_module_extensions(name)
                    index = i
                    break
            plugin_module = imp.load_module(_PLUGIN_MODULE_PREFIX + name, *info)
            plugin = PluginWrapper(plugin_module, plugindir, file=info[1])
            versions = [version_from_string(v) for v in
                        list(plugin.api_versions)]
            compatible_versions = list(set(versions) & self._api_versions)
            if compatible_versions:
                log.debug("Loading plugin %r version %s, compatible with API: %s",
                          plugin.name,
                          plugin.version,
                          ", ".join([version_to_string(v, short=True) for v in
                                     sorted(compatible_versions)]))
                plugin.compatible = True
                setattr(picard.plugins, name, plugin_module)
                if index is not None:
                    self.plugins[index] = plugin
                else:
                    self.plugins.append(plugin)
            else:
                log.warning("Plugin '%s' from '%s' is not compatible"
                            " with this version of Picard."
                            % (plugin.name, plugin.file))
        except VersionError as e:
            log.error("Plugin %r has an invalid API version string : %s", name, e)
        except:
            log.error("Plugin %r : %s", name, traceback.format_exc())
        if info[0] is not None:
            info[0].close()
        return plugin
Example #43
0
def zip_import(path):
    if (not is_zip(path) or not os.path.isfile(path)):
        return (None, None, None)
    try:
        zip_importer = zipimport.zipimporter(path)
        plugin_name = _plugin_name_from_path(path)
        manifest_data = None
        if is_zipped_package(path):
            try:
                manifest_data = load_manifest(path)
            except Exception as why:
                log.warning("Failed to load manifest data from json: %s", why)
        return (zip_importer, plugin_name, manifest_data)
    except zipimport.ZipImportError:
        return (None, None, None)
Example #44
0
 def load_plugindir(self, plugindir):
     plugindir = os.path.normpath(plugindir)
     if not os.path.isdir(plugindir):
         log.warning("Plugin directory %r doesn't exist", plugindir)
         return
     names = set()
     for path in [os.path.join(plugindir, file) for file in os.listdir(plugindir)]:
         name = _plugin_name_from_path(path)
         if name:
             names.add(name)
     log.debug("Looking for plugins in directory %r, %d names found",
               plugindir,
               len(names))
     for name in sorted(names):
         self.load_plugin(name, plugindir)
Example #45
0
def check_io_encoding():
    if _io_encoding == "ANSI_X3.4-1968":
        from picard import log
        log.warning("""
System locale charset is ANSI_X3.4-1968
Your system's locale charset (i.e. the charset used to encode filenames)
is set to ANSI_X3.4-1968. It is highly unlikely that this has been done
intentionally. Most likely the locale is not set at all. An invalid setting
will result in problems when creating data projects.
To properly set the locale charset make sure the LC_* environment variables
are set. Normally the distribution setup tools take care of this.

Translation: Picard will have problems with non-english characters
               in filenames until you change your charset.
""")
Example #46
0
 def _save_and_rename(self, old_filename, metadata):
     """Save the metadata."""
     # Check that file has not been removed since thread was queued
     # Also don't save if we are stopping.
     if self.state == File.REMOVED:
         log.debug("File not saved because it was removed: %r", self.filename)
         return None
     if self.tagger.stopping:
         log.debug("File not saved because %s is stopping: %r", PICARD_APP_NAME, self.filename)
         return None
     new_filename = old_filename
     if not config.setting["dont_write_tags"]:
         encoded_old_filename = encode_filename(old_filename)
         info = os.stat(encoded_old_filename)
         self._save(old_filename, metadata)
         if config.setting["preserve_timestamps"]:
             try:
                 os.utime(encoded_old_filename, (info.st_atime, info.st_mtime))
             except OSError:
                 log.warning("Couldn't preserve timestamp for %r", old_filename)
     # Rename files
     if config.setting["rename_files"] or config.setting["move_files"]:
         new_filename = self._rename(old_filename, metadata)
     # Move extra files (images, playlists, etc.)
     if config.setting["move_files"] and config.setting["move_additional_files"]:
         self._move_additional_files(old_filename, new_filename)
     # Delete empty directories
     if config.setting["delete_empty_dirs"]:
         dirname = encode_filename(os.path.dirname(old_filename))
         try:
             self._rmdir(dirname)
             head, tail = os.path.split(dirname)
             if not tail:
                 head, tail = os.path.split(head)
             while head and tail:
                 try:
                     self._rmdir(head)
                 except:
                     break
                 head, tail = os.path.split(head)
         except EnvironmentError:
             pass
     # Save cover art images
     if config.setting["save_images_to_files"]:
         self._save_images(os.path.dirname(new_filename), metadata)
     return new_filename
Example #47
0
 def install_plugin(self, path, dest):
     plugin_name = _plugin_name_from_path(path)
     if plugin_name:
         try:
             dest_exists = os.path.exists(dest)
             same_file = os_path_samefile(path, dest) if dest_exists else False
             if os.path.isfile(path) and not (dest_exists and same_file):
                 shutil.copy(path, dest)
             elif os.path.isdir(path) and not same_file:
                 if dest_exists:
                     shutil.rmtree(dest)
                 shutil.copytree(path, dest)
             plugin = self.load_plugin(plugin_name, USER_PLUGIN_DIR)
             if plugin is not None:
                 self.plugin_installed.emit(plugin, False)
         except (OSError, IOError):
             log.warning("Unable to copy %s to plugin folder %s" % (path, USER_PLUGIN_DIR))
Example #48
0
    def _display_artwork_tab(self):
        tab = self.ui.artwork_tab
        images = self.obj.metadata.images
        if not images:
            self.tab_hide(tab)
            return

        self.ui.artwork_list.itemDoubleClicked.connect(self.show_item)
        for image in images:
            data = None
            try:
                if image.thumbnail:
                    try:
                        data = image.thumbnail.data
                    except CoverArtImageIOError as e:
                        log.warning(unicode(e))
                        pass
                else:
                    data = image.data
            except CoverArtImageIOError:
                log.error(traceback.format_exc())
                continue
            item = QtGui.QListWidgetItem()
            item.setData(QtCore.Qt.UserRole, image)
            if data is not None:
                pixmap = QtGui.QPixmap()
                pixmap.loadFromData(data)
                icon = QtGui.QIcon(pixmap)
                item.setIcon(icon)
                item.setToolTip(
                    _("Double-click to open in external viewer\n"
                      "Temporary file: %s\n"
                      "Source: %s") % (image.tempfile_filename, image.source))
            infos = []
            infos.append(image.types_as_string())
            if image.comment:
                infos.append(image.comment)
            infos.append(u"%s (%s)" %
                         (bytes2human.decimal(image.datalength),
                          bytes2human.binary(image.datalength)))
            if image.width and image.height:
                infos.append(u"%d x %d" % (image.width, image.height))
            infos.append(image.mimetype)
            item.setText(u"\n".join(infos))
            self.ui.artwork_list.addItem(item)
Example #49
0
 def on_remote_image_fetched(self, url, data, reply, error, fallback_data=None):
     mime = reply.header(QtNetwork.QNetworkRequest.ContentTypeHeader)
     if mime in ('image/jpeg', 'image/png'):
         self.load_remote_image(url, mime, data)
     elif url.hasQueryItem("imgurl"):
         # This may be a google images result, try to get the URL which is encoded in the query
         url = QtCore.QUrl(url.queryItemValue("imgurl"))
         self.fetch_remote_image(url)
     else:
         log.warning("Can't load remote image with MIME-Type %s", mime)
         if fallback_data:
             # Tests for image format obtained from file-magic
             try:
                 mime = imageinfo.identify(fallback_data)[2]
             except imageinfo.IdentificationError as e:
                 log.error("Unable to identify dropped data format: %s" % e)
             else:
                 log.debug("Trying the dropped %s data", mime)
                 self.load_remote_image(url, mime, fallback_data)
Example #50
0
 def load_remote_image(self, mime, data):
     pixmap = QtGui.QPixmap()
     if not pixmap.loadFromData(data):
         log.warning("Can't load image")
         return
     self.__set_data([mime, data], pixmap=pixmap)
     if isinstance(self.item, Album):
         album = self.item
         album.metadata.make_and_add_image(mime, data)
         for track in album.tracks:
             track.metadata.make_and_add_image(mime, data)
         for file in album.iterfiles():
             file.metadata.make_and_add_image(mime, data)
     elif isinstance(self.item, Track):
         track = self.item
         track.metadata.make_and_add_image(mime, data)
         for file in track.iterfiles():
             file.metadata.make_and_add_image(mime, data)
     elif isinstance(self.item, File):
         file = self.item
         file.metadata.make_and_add_image(mime, data)
Example #51
0
 def _save_and_rename(self, old_filename, metadata):
     """Save the metadata."""
     new_filename = old_filename
     if not config.setting["dont_write_tags"]:
         encoded_old_filename = encode_filename(old_filename)
         info = os.stat(encoded_old_filename)
         self._save(old_filename, metadata)
         if config.setting["preserve_timestamps"]:
             try:
                 os.utime(encoded_old_filename, (info.st_atime, info.st_mtime))
             except OSError:
                 log.warning("Couldn't preserve timestamp for %r", old_filename)
     # Rename files
     if config.setting["rename_files"] or config.setting["move_files"]:
         new_filename = self._rename(old_filename, metadata)
     # Move extra files (images, playlists, etc.)
     if config.setting["move_files"] and config.setting["move_additional_files"]:
         self._move_additional_files(old_filename, new_filename)
     # Delete empty directories
     if config.setting["delete_empty_dirs"]:
         dirname = encode_filename(os.path.dirname(old_filename))
         try:
             self._rmdir(dirname)
             head, tail = os.path.split(dirname)
             if not tail:
                 head, tail = os.path.split(head)
             while head and tail:
                 try:
                     self._rmdir(head)
                 except:
                     break
                 head, tail = os.path.split(head)
         except EnvironmentError:
             pass
     # Save cover art images
     if config.setting["save_images_to_files"]:
         self._save_images(os.path.dirname(new_filename), metadata)
     return new_filename
Example #52
0
File: plugin.py Project: zas/picard
    def install_plugin(self, path, update=False, overwrite_confirm=None, plugin_name=None,
                       plugin_data=None):
        """
            path is either:
                1) /some/dir/name.py
                2) /some/dir/name (directory containing __init__.py)
                3) /some/dir/name.zip (containing either 1 or 2)

        """
        zip_plugin = False
        if not plugin_name:
            zip_plugin = is_zip(path)
            if not zip_plugin:
                plugin_name = _plugin_name_from_path(path)
            else:
                plugin_name = os.path.splitext(zip_plugin)[0]
        if plugin_name:
            try:
                if plugin_data and plugin_name:
                    # zipped module from download
                    zip_plugin = plugin_name + '.zip'
                    dst = os.path.join(USER_PLUGIN_DIR, zip_plugin)
                    if update:
                        dst += '.update'
                        if os.path.isfile(dst):
                            os.remove(dst)
                    ziptmp = tempfile.NamedTemporaryFile(delete=False,
                                                         dir=USER_PLUGIN_DIR).name
                    try:
                        with open(ziptmp, "wb") as zipfile:
                            zipfile.write(plugin_data)
                            zipfile.flush()
                            os.fsync(zipfile.fileno())
                        os.rename(ziptmp, dst)
                        log.debug("Plugin saved to %r", dst)
                    except BaseException:
                        try:
                            os.remove(ziptmp)
                        except (IOError, OSError):
                            pass
                        raise
                elif os.path.isfile(path):
                    dst = os.path.join(USER_PLUGIN_DIR, os.path.basename(path))
                    if update:
                        dst += '.update'
                        if os.path.isfile(dst):
                            os.remove(dst)
                    shutil.copy2(path, dst)
                elif os.path.isdir(path):
                    dst = os.path.join(USER_PLUGIN_DIR, plugin_name)
                    if update:
                        dst += '.update'
                        if os.path.isdir(dst):
                            shutil.rmtree(dst)
                    shutil.copytree(path, dst)
                if not update:
                    try:
                        installed_plugin = self.load_plugin(zip_plugin or plugin_name, USER_PLUGIN_DIR)
                    except Exception as e:
                        log.error('Unable to load plugin: %s.\nError occured: %s', plugin_name, e)
                        installed_plugin = None

                    if installed_plugin is not None:
                        self.plugin_installed.emit(installed_plugin, False)
                else:
                    self.plugin_updated.emit(plugin_name, False)
            except (OSError, IOError):
                log.warning("Unable to copy %s to plugin folder %s" % (path, USER_PLUGIN_DIR))
Example #53
0
    def create_actions(self):
        self.options_action = QtWidgets.QAction(icontheme.lookup('preferences-desktop'), _("&Options..."), self)
        self.options_action.setMenuRole(QtWidgets.QAction.PreferencesRole)
        self.options_action.triggered.connect(self.show_options)

        self.cut_action = QtWidgets.QAction(icontheme.lookup('edit-cut', icontheme.ICON_SIZE_MENU), _("&Cut"), self)
        self.cut_action.setShortcut(QtGui.QKeySequence.Cut)
        self.cut_action.setEnabled(False)
        self.cut_action.triggered.connect(self.cut)

        self.paste_action = QtWidgets.QAction(icontheme.lookup('edit-paste', icontheme.ICON_SIZE_MENU), _("&Paste"), self)
        self.paste_action.setShortcut(QtGui.QKeySequence.Paste)
        self.paste_action.setEnabled(False)
        self.paste_action.triggered.connect(self.paste)

        self.help_action = QtWidgets.QAction(_("&Help..."), self)
        self.help_action.setShortcut(QtGui.QKeySequence.HelpContents)
        self.help_action.triggered.connect(self.show_help)

        self.about_action = QtWidgets.QAction(_("&About..."), self)
        self.about_action.setMenuRole(QtWidgets.QAction.AboutRole)
        self.about_action.triggered.connect(self.show_about)

        self.donate_action = QtWidgets.QAction(_("&Donate..."), self)
        self.donate_action.triggered.connect(self.open_donation_page)

        self.report_bug_action = QtWidgets.QAction(_("&Report a Bug..."), self)
        self.report_bug_action.triggered.connect(self.open_bug_report)

        self.support_forum_action = QtWidgets.QAction(_("&Support Forum..."), self)
        self.support_forum_action.triggered.connect(self.open_support_forum)

        self.add_files_action = QtWidgets.QAction(icontheme.lookup('document-open'), _("&Add Files..."), self)
        self.add_files_action.setStatusTip(_("Add files to the tagger"))
        # TR: Keyboard shortcut for "Add Files..."
        self.add_files_action.setShortcut(QtGui.QKeySequence.Open)
        self.add_files_action.triggered.connect(self.add_files)

        self.add_directory_action = QtWidgets.QAction(icontheme.lookup('folder'), _("A&dd Folder..."), self)
        self.add_directory_action.setStatusTip(_("Add a folder to the tagger"))
        # TR: Keyboard shortcut for "Add Directory..."
        self.add_directory_action.setShortcut(QtGui.QKeySequence(_("Ctrl+D")))
        self.add_directory_action.triggered.connect(self.add_directory)

        if self.show_close_window:
            self.close_window_action = QtWidgets.QAction(_("Close Window"), self)
            self.close_window_action.setShortcut(QtGui.QKeySequence(_("Ctrl+W")))
            self.close_window_action.triggered.connect(self.close_active_window)

        self.save_action = QtWidgets.QAction(icontheme.lookup('document-save'), _("&Save"), self)
        self.save_action.setStatusTip(_("Save selected files"))
        # TR: Keyboard shortcut for "Save"
        self.save_action.setShortcut(QtGui.QKeySequence.Save)
        self.save_action.setEnabled(False)
        self.save_action.triggered.connect(self.save)

        self.submit_acoustid_action = QtWidgets.QAction(icontheme.lookup('acoustid-fingerprinter'), _("S&ubmit AcoustIDs"), self)
        self.submit_acoustid_action.setStatusTip(_("Submit acoustic fingerprints"))
        self.submit_acoustid_action.setEnabled(False)
        self.submit_acoustid_action.triggered.connect(self._on_submit_acoustid)

        self.exit_action = QtWidgets.QAction(_("E&xit"), self)
        self.exit_action.setMenuRole(QtWidgets.QAction.QuitRole)
        # TR: Keyboard shortcut for "Exit"
        self.exit_action.setShortcut(QtGui.QKeySequence(_("Ctrl+Q")))
        self.exit_action.triggered.connect(self.close)

        self.remove_action = QtWidgets.QAction(icontheme.lookup('list-remove'), _("&Remove"), self)
        self.remove_action.setStatusTip(_("Remove selected files/albums"))
        self.remove_action.setEnabled(False)
        self.remove_action.triggered.connect(self.remove)

        self.browser_lookup_action = QtWidgets.QAction(icontheme.lookup('lookup-musicbrainz'), _("Lookup in &Browser"), self)
        self.browser_lookup_action.setStatusTip(_("Lookup selected item on MusicBrainz website"))
        self.browser_lookup_action.setEnabled(False)
        # TR: Keyboard shortcut for "Lookup in Browser"
        self.browser_lookup_action.setShortcut(QtGui.QKeySequence(_("Ctrl+Shift+L")))
        self.browser_lookup_action.triggered.connect(self.browser_lookup)

        self.album_search_action = QtWidgets.QAction(icontheme.lookup('system-search'), _("Search for similar albums..."), self)
        self.album_search_action.setStatusTip(_("View similar releases and optionally choose a different release"))
        self.album_search_action.triggered.connect(self.show_more_albums)

        self.track_search_action = QtWidgets.QAction(icontheme.lookup('system-search'), _("Search for similar tracks..."), self)
        self.track_search_action.setStatusTip(_("View similar tracks and optionally choose a different release"))
        self.track_search_action.triggered.connect(self.show_more_tracks)

        self.show_file_browser_action = QtWidgets.QAction(_("File &Browser"), self)
        self.show_file_browser_action.setCheckable(True)
        if config.persist["view_file_browser"]:
            self.show_file_browser_action.setChecked(True)
        self.show_file_browser_action.setShortcut(QtGui.QKeySequence(_("Ctrl+B")))
        self.show_file_browser_action.triggered.connect(self.show_file_browser)

        self.show_cover_art_action = QtWidgets.QAction(_("&Cover Art"), self)
        self.show_cover_art_action.setCheckable(True)
        if config.persist["view_cover_art"]:
            self.show_cover_art_action.setChecked(True)
        self.show_cover_art_action.triggered.connect(self.show_cover_art)

        self.show_toolbar_action = QtWidgets.QAction(_("&Actions"), self)
        self.show_toolbar_action.setCheckable(True)
        if config.persist["view_toolbar"]:
            self.show_toolbar_action.setChecked(True)
        self.show_toolbar_action.triggered.connect(self.show_toolbar)

        self.search_action = QtWidgets.QAction(icontheme.lookup('system-search'), _("Search"), self)
        self.search_action.setEnabled(False)
        self.search_action.triggered.connect(self.search)

        self.cd_lookup_action = QtWidgets.QAction(icontheme.lookup('media-optical'), _("Lookup &CD..."), self)
        self.cd_lookup_action.setStatusTip(_("Lookup the details of the CD in your drive"))
        # TR: Keyboard shortcut for "Lookup CD"
        self.cd_lookup_action.setShortcut(QtGui.QKeySequence(_("Ctrl+K")))
        self.cd_lookup_action.triggered.connect(self.tagger.lookup_cd)

        self.cd_lookup_menu = QtWidgets.QMenu(_("Lookup &CD..."))
        self.cd_lookup_menu.triggered.connect(self.tagger.lookup_cd)
        self.cd_lookup_action.setEnabled(False)
        if discid is None:
            log.warning("CDROM: discid library not found - Lookup CD functionality disabled")
        else:
            drives = get_cdrom_drives()
            if not drives:
                log.warning("CDROM: No CD-ROM drives found - Lookup CD functionality disabled")
            else:
                shortcut_drive = config.setting["cd_lookup_device"].split(",")[0] if len(drives) > 1 else ""
                self.cd_lookup_action.setEnabled(True)
                for drive in drives:
                    action = self.cd_lookup_menu.addAction(drive)
                    action.setData(drive)
                    if drive == shortcut_drive:
                        # Clear existing shortcode on main action and assign it to sub-action
                        self.cd_lookup_action.setShortcut(QtGui.QKeySequence())
                        action.setShortcut(QtGui.QKeySequence(_("Ctrl+K")))

        self.analyze_action = QtWidgets.QAction(icontheme.lookup('picard-analyze'), _("&Scan"), self)
        self.analyze_action.setStatusTip(_("Use AcoustID audio fingerprint to identify the files by the actual music, even if they have no metadata"))
        self.analyze_action.setEnabled(False)
        self.analyze_action.setToolTip(_('Identify the file using its AcoustID audio fingerprint'))
        # TR: Keyboard shortcut for "Analyze"
        self.analyze_action.setShortcut(QtGui.QKeySequence(_("Ctrl+Y")))
        self.analyze_action.triggered.connect(self.analyze)

        self.cluster_action = QtWidgets.QAction(icontheme.lookup('picard-cluster'), _("Cl&uster"), self)
        self.cluster_action.setStatusTip(_("Cluster files into album clusters"))
        self.cluster_action.setEnabled(False)
        # TR: Keyboard shortcut for "Cluster"
        self.cluster_action.setShortcut(QtGui.QKeySequence(_("Ctrl+U")))
        self.cluster_action.triggered.connect(self.cluster)

        self.autotag_action = QtWidgets.QAction(icontheme.lookup('picard-auto-tag'), _("&Lookup"), self)
        tip = _("Lookup selected items in MusicBrainz")
        self.autotag_action.setToolTip(tip)
        self.autotag_action.setStatusTip(tip)
        self.autotag_action.setEnabled(False)
        # TR: Keyboard shortcut for "Lookup"
        self.autotag_action.setShortcut(QtGui.QKeySequence(_("Ctrl+L")))
        self.autotag_action.triggered.connect(self.autotag)

        self.view_info_action = QtWidgets.QAction(icontheme.lookup('picard-edit-tags'), _("&Info..."), self)
        self.view_info_action.setEnabled(False)
        # TR: Keyboard shortcut for "Info"
        self.view_info_action.setShortcut(QtGui.QKeySequence(_("Ctrl+I")))
        self.view_info_action.triggered.connect(self.view_info)

        self.refresh_action = QtWidgets.QAction(icontheme.lookup('view-refresh', icontheme.ICON_SIZE_MENU), _("&Refresh"), self)
        self.refresh_action.setShortcut(QtGui.QKeySequence(_("Ctrl+R")))
        self.refresh_action.triggered.connect(self.refresh)

        self.enable_renaming_action = QtWidgets.QAction(_("&Rename Files"), self)
        self.enable_renaming_action.setCheckable(True)
        self.enable_renaming_action.setChecked(config.setting["rename_files"])
        self.enable_renaming_action.triggered.connect(self.toggle_rename_files)

        self.enable_moving_action = QtWidgets.QAction(_("&Move Files"), self)
        self.enable_moving_action.setCheckable(True)
        self.enable_moving_action.setChecked(config.setting["move_files"])
        self.enable_moving_action.triggered.connect(self.toggle_move_files)

        self.enable_tag_saving_action = QtWidgets.QAction(_("Save &Tags"), self)
        self.enable_tag_saving_action.setCheckable(True)
        self.enable_tag_saving_action.setChecked(not config.setting["dont_write_tags"])
        self.enable_tag_saving_action.triggered.connect(self.toggle_tag_saving)

        self.tags_from_filenames_action = QtWidgets.QAction(_("Tags From &File Names..."), self)
        self.tags_from_filenames_action.triggered.connect(self.open_tags_from_filenames)
        self.tags_from_filenames_action.setEnabled(False)

        self.open_collection_in_browser_action = QtWidgets.QAction(_("&Open My Collections in Browser"), self)
        self.open_collection_in_browser_action.triggered.connect(self.open_collection_in_browser)
        self.open_collection_in_browser_action.setEnabled(config.setting["username"] != '')

        self.view_log_action = QtWidgets.QAction(_("View &Error/Debug Log"), self)
        self.view_log_action.triggered.connect(self.show_log)
        # TR: Keyboard shortcut for "View Error/Debug Log"
        self.view_log_action.setShortcut(QtGui.QKeySequence(_("Ctrl+E")))

        self.view_history_action = QtWidgets.QAction(_("View Activity &History"), self)
        self.view_history_action.triggered.connect(self.show_history)
        # TR: Keyboard shortcut for "View Activity History"
        self.view_history_action.setShortcut(QtGui.QKeySequence(_("Ctrl+H")))

        webservice_manager = self.tagger.webservice.manager
        webservice_manager.authenticationRequired.connect(self.show_password_dialog)
        webservice_manager.proxyAuthenticationRequired.connect(self.show_proxy_dialog)

        self.play_file_action = QtWidgets.QAction(icontheme.lookup('play-music'), _("Open in &Player"), self)
        self.play_file_action.setStatusTip(_("Play the file in your default media player"))
        self.play_file_action.setEnabled(False)
        self.play_file_action.triggered.connect(self.play_file)

        self.open_folder_action = QtWidgets.QAction(icontheme.lookup('folder', icontheme.ICON_SIZE_MENU), _("Open Containing &Folder"), self)
        self.open_folder_action.setStatusTip(_("Open the containing folder in your file explorer"))
        self.open_folder_action.setEnabled(False)
        self.open_folder_action.triggered.connect(self.open_folder)

        if self.tagger.autoupdate_enabled:
            self.check_update_action = QtWidgets.QAction(_("&Check for Update…"), self)
            self.check_update_action.setMenuRole(QtWidgets.QAction.ApplicationSpecificRole)
            self.check_update_action.triggered.connect(self.do_update_check)
Example #54
0
    def _load_plugin_from_directory(self, name, plugindir):
        module_file = None
        (zip_importer, module_name, manifest_data) = zip_import(os.path.join(plugindir, name + '.zip'))
        if zip_importer:
            name = module_name
            if not zip_importer.find_module(name):
                error = _("Failed loading zipped plugin %r") % name
                self.plugin_error(name, error)
                return None
            module_pathname = zip_importer.get_filename(name)
        else:
            try:
                info = imp.find_module(name, [plugindir])
                module_file = info[0]
                module_pathname = info[1]
            except ImportError:
                error = _("Failed loading plugin %r") % name
                self.plugin_error(name, error)
                return None

        plugin = None
        try:
            existing_plugin, existing_plugin_index = self._get_plugin_index_by_name(name)
            if existing_plugin:
                log.warning("Module %r conflict: unregistering previously"
                            " loaded %r version %s from %r",
                            existing_plugin.module_name,
                            existing_plugin.name,
                            existing_plugin.version,
                            existing_plugin.file)
                _unregister_module_extensions(name)
            full_module_name = _PLUGIN_MODULE_PREFIX + name
            if zip_importer:
                plugin_module = zip_importer.load_module(full_module_name)
            else:
                plugin_module = imp.load_module(full_module_name, *info)
            plugin = PluginWrapper(plugin_module, plugindir,
                                   file=module_pathname, manifest_data=manifest_data)
            compatible_versions = _compatible_api_versions(plugin.api_versions)
            if compatible_versions:
                log.debug("Loading plugin %r version %s, compatible with API: %s",
                          plugin.name,
                          plugin.version,
                          ", ".join([version_to_string(v, short=True) for v in
                                     sorted(compatible_versions)]))
                plugin.compatible = True
                setattr(picard.plugins, name, plugin_module)
                if existing_plugin:
                    self.plugins[existing_plugin_index] = plugin
                else:
                    self.plugins.append(plugin)
            else:
                error = _("Plugin '%s' from '%s' is not compatible with this "
                          "version of Picard.") % (plugin.name, plugin.file)
                self.plugin_error(plugin.name, error, log_func=log.warning)
        except VersionError as e:
            error = _("Plugin %r has an invalid API version string : %s") % (name, e)
            self.plugin_error(name, error)
        except BaseException:
            error = _("Plugin %r : %s") % (name, traceback.format_exc())
            self.plugin_error(name, error)
        if module_file is not None:
            module_file.close()
        return plugin
Example #55
0
 def on_remote_image_fetched(self, data, reply, error):
     mime = str(reply.header(QtNetwork.QNetworkRequest.ContentTypeHeader).toString())
     if mime not in ('image/jpeg', 'image/png'):
         log.warning("Can't load image with MIME-Type %s", mime)
         return
     return self.load_remote_image(mime, data)
Example #56
0
def convert_to_string(obj):
    from picard import log
    log.warning("string_() and convert_to_string() are deprecated, do not use")
    return __convert_to_string(obj)
Example #57
0
from picard.album import Album, NatAlbum
from picard.cluster import Cluster, ClusterList, UnmatchedFiles
from picard.file import File
from picard.track import Track, NonAlbumTrack
from picard.util import encode_filename, icontheme
from picard.plugin import ExtensionPoint
from picard.ui.ratingwidget import RatingWidget
from picard.ui.collectionmenu import CollectionMenu

if sys.platform == 'darwin':
    try:
        from Foundation import NSURL
        NSURL_IMPORTED = True     
    except ImportError:
        NSURL_IMPORTED = False     
        log.warning("Unable to import NSURL, file drag'n'drop might not work correctly")       


class BaseAction(QtGui.QAction):
    NAME = "Unknown"
    MENU = []

    def __init__(self):
        QtGui.QAction.__init__(self, self.NAME, None)
        self.triggered.connect(self.__callback)

    def __callback(self):
        objs = self.tagger.window.selected_objects
        self.callback(objs)

    def callback(self, objs):
            continue
        mainkey, subkey = key.split(':', 1)
        if not subkey:
            continue
        instruments = standardise_performers_split(subkey)
        if len(instruments) == 1:
            continue
        log.debug("%s: Splitting Performer [%s] into separate performers",
            PLUGIN_NAME,
            subkey,
            )
        for instrument in instruments:
            newkey = '%s:%s' % (mainkey, instrument)
            for value in values:
                metadata.add_unique(newkey, value)
        del metadata[key]


try:
    from picard.plugin import PluginPriority

    register_track_metadata_processor(standardise_performers,
                                      priority=PluginPriority.HIGH)
except ImportError:
    log.warning(
        "Running %r plugin on this Picard version may not work as you expect. "
        "Any other plugins that run before it will get the old performers "
        "rather than the standardized performers.", PLUGIN_NAME
    )
    register_track_metadata_processor(standardise_performers)