Пример #1
0
    def _handle_redirect(self, reply, request, redirect):
        url = request.url()
        error = int(reply.error())
        # merge with base url (to cover the possibility of the URL being relative)
        redirect = url.resolved(redirect)
        if not WebService.urls_equivalent(redirect, reply.request().url()):
            log.debug("Redirect to %s requested", redirect.toString(QUrl.RemoveUserInfo))
            redirect_host = string_(redirect.host())
            redirect_port = self.url_port(redirect)
            redirect_query = dict(QUrlQuery(redirect).queryItems(QUrl.FullyEncoded))
            redirect_path = redirect.path()

            original_host = string_(url.host())
            original_port = self.url_port(url)
            original_host_key = (original_host, original_port)
            redirect_host_key = (redirect_host, redirect_port)
            if (original_host_key in REQUEST_DELAY_MINIMUM
                    and redirect_host_key not in REQUEST_DELAY_MINIMUM):
                log.debug("Setting the minimum rate limit for %s to %i" %
                          (redirect_host_key, REQUEST_DELAY_MINIMUM[original_host_key]))
                REQUEST_DELAY_MINIMUM[redirect_host_key] = REQUEST_DELAY_MINIMUM[original_host_key]

            self.get(redirect_host,
                     redirect_port,
                     redirect_path,
                     request.handler, request.parse_response_type, priority=True, important=True,
                     refresh=request.refresh, queryargs=redirect_query,
                     cacheloadcontrol=request.attribute(QtNetwork.QNetworkRequest.CacheLoadControlAttribute))
        else:
            log.error("Redirect loop: %s",
                      reply.request().url().toString(QUrl.RemoveUserInfo)
                      )
            request.handler(reply.readAll(), reply, error)
Пример #2
0
 def load_plugindir(self, plugindir):
     plugindir = os.path.normpath(plugindir)
     if not os.path.isdir(plugindir):
         log.info("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):
         try:
             self.load_plugin(name, plugindir)
         except Exception as e:
             log.error('Unable to load plugin: %s.\nError occured: %s', name, e)
Пример #3
0
    def _handle_redirect(self, reply, request, redirect):
        url = request.url()
        error = int(reply.error())
        # merge with base url (to cover the possibility of the URL being relative)
        redirect = url.resolved(redirect)
        if not WebService.urls_equivalent(redirect, reply.request().url()):
            log.debug("Redirect to %s requested", redirect.toString(QUrl.RemoveUserInfo))
            redirect_host = redirect.host()
            redirect_port = self.url_port(redirect)
            redirect_query = dict(QUrlQuery(redirect).queryItems(QUrl.FullyEncoded))
            redirect_path = redirect.path()

            original_host = url.host()
            original_port = self.url_port(url)
            original_host_key = (original_host, original_port)
            redirect_host_key = (redirect_host, redirect_port)
            ratecontrol.copy_minimal_delay(original_host_key, redirect_host_key)

            self.get(redirect_host,
                     redirect_port,
                     redirect_path,
                     request.handler, request.parse_response_type, priority=True, important=True,
                     refresh=request.refresh, queryargs=redirect_query, mblogin=request.mblogin,
                     cacheloadcontrol=request.attribute(QNetworkRequest.CacheLoadControlAttribute))
        else:
            log.error("Redirect loop: %s",
                      reply.request().url().toString(QUrl.RemoveUserInfo)
                      )
            request.handler(reply.readAll(), reply, error)
Пример #4
0
    def _display_artwork_tab(self):
        tab = self.ui.artwork_tab
        images = self.obj.metadata.images
        if not images:
            self.tab_hide(tab)
            return

        for image in images:
            try:
                data = image.data
            except (OSError, IOError) as e:
                log.error(traceback.format_exc())
                continue
            size = len(data)
            item = QtGui.QListWidgetItem()
            pixmap = QtGui.QPixmap()
            pixmap.loadFromData(data)
            icon = QtGui.QIcon(pixmap)
            item.setIcon(icon)
            s = "%s (%s)\n%d x %d" % (bytes2human.decimal(size),
                                      bytes2human.binary(size),
                                      pixmap.width(),
                                      pixmap.height())
            item.setText(s)
            self.ui.artwork_list.addItem(item)
Пример #5
0
 def _move_additional_files(self, old_filename, new_filename):
     """Move extra files, like playlists..."""
     old_path = os.path.dirname(old_filename)
     new_path = os.path.dirname(new_filename)
     try:
         names = set(os.listdir(old_path))
     except os.error:
         log.error("Error: {} directory not found".naming_format(old_path))
         return
     filtered_names = {name for name in names if name[0] != "."}
     for pattern in config.setting["move_additional_files_pattern"].split():
         pattern = pattern.strip()
         if not pattern:
             continue
         pattern_regex = re.compile(fnmatch.translate(pattern), re.IGNORECASE)
         file_names = names
         if pattern[0] != '.':
             file_names = filtered_names
         for old_file in set(file_names):
             if pattern_regex.match(old_file):
                 names.discard(old_file)
                 filtered_names.discard(old_file)
                 new_file = os.path.join(new_path, old_file)
                 old_file = os.path.join(old_path, old_file)
                 # FIXME we shouldn't do this from a thread!
                 if self.tagger.files.get(decode_filename(old_file)):
                     log.debug("File loaded in the tagger, not moving %r", old_file)
                     continue
                 log.debug("Moving %r to %r", old_file, new_file)
                 shutil.move(old_file, new_file)
Пример #6
0
    def _json_downloaded(self, release_group_id, data, reply, error):
        self.album._requests -= 1

        if error:
            if error != QNetworkReply.ContentNotFoundError:
                error_level = log.error
            else:
                error_level = log.debug
            error_level("Problem requesting metadata in fanart.tv plugin: %s", error)
        else:
            try:
                response = json.loads(data)
                release = response["albums"][release_group_id]

                if "albumcover" in release:
                    covers = release["albumcover"]
                    types = ["front"]
                    self._select_and_add_cover_art(covers, types)

                if "cdart" in release and \
                    (config.setting["fanarttv_use_cdart"] == OPTION_CDART_ALWAYS
                        or (config.setting["fanarttv_use_cdart"] == OPTION_CDART_NOALBUMART
                            and "albumcover" not in release)):
                    covers = release["cdart"]
                    types = ["medium"]
                    if not "albumcover" in release:
                        types.append("front")
                    self._select_and_add_cover_art(covers, types)
            except:
                log.error("Problem processing downloaded metadata in fanart.tv plugin: %s", traceback.format_exc())

        self.next_in_queue()
Пример #7
0
    def _handle_reply(self, reply, request, handler, xml, refresh):
        error = int(reply.error())
        if error:
            log.error("Network request error for %s: %s (QT code %d, HTTP code %s)",
                      reply.request().url().toString(QUrl.RemoveUserInfo),
                      reply.errorString(),
                      error,
                      repr(reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute))
                      )
            if handler is not None:
                handler(str(reply.readAll()), reply, error)
        else:
            redirect = reply.attribute(QtNetwork.QNetworkRequest.RedirectionTargetAttribute)
            fromCache = reply.attribute(QtNetwork.QNetworkRequest.SourceIsFromCacheAttribute)
            cached = ' (CACHED)' if fromCache else ''
            log.debug("Received reply for %s: HTTP %d (%s) %s",
                      reply.request().url().toString(QUrl.RemoveUserInfo),
                      reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute),
                      reply.attribute(QtNetwork.QNetworkRequest.HttpReasonPhraseAttribute),
                      cached
                      )
            if handler is not None:
                # Redirect if found and not infinite
                if redirect:
                    url = request.url()
                    # merge with base url (to cover the possibility of the URL being relative)
                    redirect = url.resolved(redirect)
                    if not XmlWebService.urls_equivalent(redirect, reply.request().url()):
                        log.debug("Redirect to %s requested", redirect.toString(QUrl.RemoveUserInfo))
                        redirect_host = str(redirect.host())
                        redirect_port = redirect.port(80)
                        redirect_query = dict(redirect.queryItems())
                        redirect_path = redirect.path()

                        original_host = str(url.host())
                        original_port = url.port(80)

                        if ((original_host, original_port) in REQUEST_DELAY
                                and (redirect_host, redirect_port) not in REQUEST_DELAY):
                            log.debug("Setting rate limit for %s:%i to %i" %
                                      (redirect_host, redirect_port,
                                       REQUEST_DELAY[(original_host, original_port)]))
                            REQUEST_DELAY[(redirect_host, redirect_port)] =\
                                REQUEST_DELAY[(original_host, original_port)]

                        self.get(redirect_host,
                                 redirect_port,
                                 redirect_path,
                                 handler, xml, priority=True, important=True, refresh=refresh, queryargs=redirect_query,
                                 cacheloadcontrol=request.attribute(QtNetwork.QNetworkRequest.CacheLoadControlAttribute))
                    else:
                        log.error("Redirect loop: %s",
                                  reply.request().url().toString(QUrl.RemoveUserInfo)
                                  )
                        handler(str(reply.readAll()), reply, error)
                elif xml:
                    document = _read_xml(QXmlStreamReader(reply))
                    handler(document, reply, error)
                else:
                    handler(str(reply.readAll()), reply, error)
Пример #8
0
    def install_plugin(self, path, update=False, 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)

        """
        assert path or plugin_name, "path is required if plugin_name is empty"

        if not plugin_name:
            plugin_name = _plugin_name_from_path(path)
        if plugin_name:
            try:
                if plugin_data:
                    self._install_plugin_zip(plugin_name, plugin_data, update=update)
                elif os.path.isfile(path):
                    self._install_plugin_file(path, update=update)
                elif os.path.isdir(path):
                    self._install_plugin_dir(plugin_name, path, update=update)
            except (OSError, IOError) as why:
                log.error("Unable to copy plugin '%s' to %r: %s" % (plugin_name, self.plugins_directory, why))
                return

            if not update:
                try:
                    installed_plugin = self._load_plugin_from_directory(plugin_name, self.plugins_directory)
                except Exception as e:
                    log.error("Unable to load plugin '%s': %s", plugin_name, e)
                else:
                    self.plugin_installed.emit(installed_plugin, False)
            else:
                self.plugin_updated.emit(plugin_name, False)
Пример #9
0
def _caa_json_downloaded(album, metadata, release, try_list, data, http, error):
    album._requests -= 1
    caa_front_found = False
    if error:
        log.error(str(http.errorString()))
    else:
        try:
            caa_data = json.loads(data)
        except ValueError:
            log.debug("Invalid JSON: %s", http.url().toString())
        else:
            caa_types = config.setting["caa_image_types"].split()
            caa_types = map(unicode.lower, caa_types)
            for image in caa_data["images"]:
                if config.setting["caa_approved_only"] and not image["approved"]:
                    continue
                if not image["types"] and "unknown" in caa_types:
                    image["types"] = [u"Unknown"]
                imagetypes = map(unicode.lower, image["types"])
                for imagetype in imagetypes:
                    if imagetype == "front":
                        caa_front_found = True
                    if imagetype in caa_types:
                        _caa_append_image_to_trylist(try_list, image)
                        break

    if error or not caa_front_found:
        _fill_try_list(album, release, try_list)
    _walk_try_list(album, metadata, release, try_list)
Пример #10
0
 def _on_fpcalc_finished(self, next_func, file, exit_code, exit_status):
     process = self.sender()
     finished = process.property('picard_finished')
     if finished:
         return
     process.setProperty('picard_finished', True)
     result = None
     try:
         self._running -= 1
         self._run_next_task()
         process = self.sender()
         if exit_code == 0 and exit_status == 0:
             output = string_(process.readAllStandardOutput())
             duration = None
             fingerprint = None
             for line in output.splitlines():
                 parts = line.split('=', 1)
                 if len(parts) != 2:
                     continue
                 if parts[0] == 'DURATION':
                     duration = int(parts[1])
                 elif parts[0] == 'FINGERPRINT':
                     fingerprint = parts[1]
             if fingerprint and duration:
                 result = 'fingerprint', fingerprint, duration
         else:
             log.error(
                 "Fingerprint calculator failed exit code = %r, exit status = %r, error = %s",
                 exit_code,
                 exit_status,
                 process.errorString())
     finally:
         next_func(result)
Пример #11
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = ASF(encode_filename(filename))
        metadata = Metadata()
        for name, values in file.tags.items():
            if name == 'WM/Picture':
                for image in values:
                    (mime, data, type, description) = unpack_image(image.value)
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(type),
                            comment=description,
                            support_types=True,
                            data=data,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

                continue
            elif name not in self.__RTRANS:
                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(unicode(values[0])) / 99.0 * (config.setting['rating_steps'] - 1)))
            name = self.__RTRANS[name]
            values = filter(bool, map(unicode, values))
            if values:
                metadata[name] = values
        self._info(metadata, file)
        return metadata
Пример #12
0
    def _handle_reply(self, reply, request):
        hostkey = request.get_host_key()
        CONGESTION_UNACK[hostkey] -= 1
        log.debug("WebService: %s: outstanding reqs: %d", hostkey, CONGESTION_UNACK[hostkey])
        self._timer_run_next_task.start(0)

        slow_down = False

        error = int(reply.error())
        handler = request.handler
        if error:
            code = reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute)
            code = int(code) if code else 0
            errstr = reply.errorString()
            url = reply.request().url().toString(QUrl.RemoveUserInfo)
            log.error("Network request error for %s: %s (QT code %d, HTTP code %d)",
                      url, errstr, error, code)
            if (not request.max_retries_reached()
                and (code == 503
                     or code == 429
                     # Sometimes QT returns a http status code of 200 even when there
                     # is a service unavailable error. But it returns a QT error code
                     # of 403 when this happens
                     or error == 403
                    )
               ):
                retries = request.mark_for_retry()
                log.debug("Retrying %s (#%d)", url, retries)
                self.add_task(partial(self._start_request, request), request)

            elif handler is not None:
                handler(reply.readAll(), reply, error)

            slow_down = True
        else:
            redirect = reply.attribute(QtNetwork.QNetworkRequest.RedirectionTargetAttribute)
            fromCache = reply.attribute(QtNetwork.QNetworkRequest.SourceIsFromCacheAttribute)
            cached = ' (CACHED)' if fromCache else ''
            log.debug("Received reply for %s: HTTP %d (%s) %s",
                      reply.request().url().toString(QUrl.RemoveUserInfo),
                      reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute),
                      reply.attribute(QtNetwork.QNetworkRequest.HttpReasonPhraseAttribute),
                      cached
                      )
            if handler is not None:
                # Redirect if found and not infinite
                if redirect:
                    self._handle_redirect(reply, request, redirect)
                elif request.response_parser:
                    try:
                        document = request.response_parser(reply)
                    except Exception as e:
                        log.error("Unable to parse the response. %s", e)
                        document = reply.readAll()
                    finally:
                        handler(document, reply, error)
                else:
                    handler(reply.readAll(), reply, error)

        self._adjust_throttle(hostkey, slow_down)
Пример #13
0
 def _release_request_finished(self, document, http, error):
     if self.load_task is None:
         return
     self.load_task = None
     parsed = False
     try:
         if error:
             log.error("%r", unicode(http.errorString()))
             # Fix for broken NAT releases
             if error == QtNetwork.QNetworkReply.ContentNotFoundError:
                 nats = False
                 nat_name = config.setting["nat_name"]
                 files = list(self.unmatched_files.files)
                 for file in files:
                     trackid = file.metadata["musicbrainz_trackid"]
                     if mbid_validate(trackid) and file.metadata["album"] == nat_name:
                         nats = True
                         self.tagger.move_file_to_nat(file, trackid)
                         self.tagger.nats.update()
                 if nats and not self.get_num_unmatched_files():
                     self.tagger.remove_album(self)
                     error = False
         else:
             try:
                 parsed = self._parse_release(document)
             except:
                 error = True
                 log.error(traceback.format_exc())
     finally:
         self._requests -= 1
         if parsed or error:
             self._finalize_loading(error)
Пример #14
0
    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()
Пример #15
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)
Пример #16
0
 def __fingerprint_submission_finished(self, fingerprints, document, http, error):
     if error:
         try:
             error = load_json(document)
             message = error["error"]["message"]
         except :
             message = ""
         mparms = {
             'error': http.errorString(),
             'message': message
         }
         log.error(
             "AcoustID: submission failed with error '%(error)s': %(message)s" %
             mparms)
         self.tagger.window.set_statusbar_message(
             N_("AcoustID submission failed with error '%(error)s': %(message)s"),
             mparms,
             echo=None,
             timeout=3000
         )
     else:
         log.debug('AcoustID: successfully submitted')
         self.tagger.window.set_statusbar_message(
             N_('AcoustIDs successfully submitted.'),
             echo=None,
             timeout=3000
         )
         for submission in fingerprints:
             submission.orig_recordingid = submission.recordingid
         self._check_unsubmitted()
Пример #17
0
def _coverart_downloaded(album, metadata, release, try_list, coverinfos, data, http, error):
    album._requests -= 1

    if error or len(data) < 1000:
        if error:
            log.error(str(http.errorString()))
    else:
        QObject.tagger.window.set_statusbar_message(N_("Coverart %s downloaded"),
                http.url().toString())
        mime = mimetype.get_from_data(data, default="image/jpeg")
        filename = None
        if not is_front_image(coverinfos) and config.setting["caa_image_type_as_filename"]:
            filename = coverinfos['type']
        metadata.add_image(mime, data, filename, coverinfos)
        for track in album._new_tracks:
            track.metadata.add_image(mime, data, filename, coverinfos)

    # If the image already was a front image, there might still be some
    # other front images in the try_list - remove them.
    if is_front_image(coverinfos):
        for item in try_list[:]:
            if is_front_image(item) and 'archive.org' not in item['host']:
                # Hosts other than archive.org only provide front images
                try_list.remove(item)
    _walk_try_list(album, metadata, release, try_list)
Пример #18
0
 def _move_additional_files(self, old_filename, new_filename):
     """Move extra files, like playlists..."""
     old_path = encode_filename(os.path.dirname(old_filename))
     new_path = encode_filename(os.path.dirname(new_filename))
     patterns = encode_filename(config.setting["move_additional_files_pattern"])
     patterns = filter(bool, [p.strip() for p in patterns.split()])
     try:
         names = os.listdir(old_path)
     except os.error:
         log.error("Error: {} directory not found".format(old_path))
         return
     filtered_names = filter(lambda x: x[0] != '.', names)
     for pattern in patterns:
         pattern_regex = re.compile(fnmatch.translate(pattern), re.IGNORECASE)
         file_names = names
         if pattern[0] != '.':
             file_names = filtered_names
         for old_file in file_names:
             if pattern_regex.match(old_file):
                 new_file = os.path.join(new_path, old_file)
                 old_file = os.path.join(old_path, old_file)
                 # FIXME we shouldn't do this from a thread!
                 if self.tagger.files.get(decode_filename(old_file)):
                     log.debug("File loaded in the tagger, not moving %r", old_file)
                     continue
                 log.debug("Moving %r to %r", old_file, new_file)
                 shutil.move(old_file, new_file)
Пример #19
0
 def artist_process_metadata(self, artistId, response):
     if 'metadata' in response.children:
         if 'artist' in response.metadata[0].children:
             if 'relation_list' in response.metadata[0].artist[0].children:
                 if 'relation' in response.metadata[0].artist[0].relation_list[0].children:
                     return self.artist_process_relations(response.metadata[0].artist[0].relation_list[0].relation)
     log.error("%s: %r: MusicBrainz artist xml result not in correct format - %s", PLUGIN_NAME, artistId, response)
     return None
Пример #20
0
 def parse_wikidata_response(self,item,item_id, response, reply, error):
     genre_entries=[]
     genre_list=[]
     if error:
         log.error('WIKIDATA: error getting data from wikidata.org')
     else:
         if 'RDF' in response.children:
             node = response.RDF[0]
             for node1 in node.Description:
                 if 'about' in node1.attribs:
                     if node1.attribs.get('about') == 'http://www.wikidata.org/entity/%s' % item:
                         for key,val in node1.children.items():
                             if key=='P136':
                                 for i in val:
                                     if 'resource' in i.attribs:
                                         tmp=i.attribs.get('resource')
                                         if 'entity' ==tmp.split('/')[3] and len(tmp.split('/'))== 5:
                                             genre_id=tmp.split('/')[4]
                                             log.info('WIKIDATA: Found the wikidata id for the genre: %s' % genre_id)
                                             genre_entries.append(tmp)
                     else:
                         for tmp in genre_entries:
                             if tmp == node1.attribs.get('about'):
                                 list1=node1.children.get('name')
                                 for node2 in list1:
                                     if node2.attribs.get('lang')=='en':
                                         genre=node2.text
                                         genre_list.append(genre)
                                         log.debug('Our genre is: %s' % genre)
                                         
     self.lock.acquire()
     if len(genre_list) > 0:
         log.info('WiKIDATA: final list of wikidata id found: %s' % genre_entries)
         log.info('WIKIDATA: final list of genre: %s' % genre_list)
         
         log.debug('WIKIDATA: total items to update: %s ' % len(self.requests[item_id]))
         for metadata in self.requests[item_id]:
             new_genre=[]
             new_genre.append(metadata["genre"])
             for str in genre_list:
                 if str not in new_genre:
                     new_genre.append(str)
                     log.debug('WIKIDATA: appending genre %s' % str)
             metadata["genre"] = new_genre
             self.cache[item_id]=genre_list
             log.debug('WIKIDATA: setting genre : %s ' % genre_list)
             
     else:
         log.info('WIKIDATA: Genre not found in wikidata')
     
     log.info('WIKIDATA: Seeing if we can finalize tags %s  ' % len(self.taggers[item_id]))
     
     for tagger in self.taggers[item_id]:
         tagger._requests -= 1
         if tagger._requests==0:
             tagger._finalize_loading(None)
         log.info('WIKIDATA:  TOTAL REMAINING REQUESTS %s' % tagger._requests)
     self.lock.release()
Пример #21
0
 def event(self, event):
     if isinstance(event, ProxyToMainEvent):
         try:
             event.call()
         except:
             from picard import log
             log.error(traceback.format_exc())
         return True
     return False
Пример #22
0
 def run(self):
     try:
         result = self.func()
     except:
         from picard import log
         log.error(traceback.format_exc())
         to_main(self.next_func, error=sys.exc_info()[1])
     else:
         to_main(self.next_func, result=result)
Пример #23
0
    def _lookup_finished(self, lookuptype, document, http, error):
        self.lookup_task = None

        if self.state == File.REMOVED:
            return
        if error:
            log.error("Network error encountered during the lookup for %s. Error code: %s",
                      self.filename, error)
        try:
            if lookuptype == "metadata":
                tracks = document['recordings']
            elif lookuptype == "acoustid":
                tracks = document['recordings']
        except (KeyError, TypeError):
            tracks = None

        # no matches
        if not tracks:
            self.tagger.window.set_statusbar_message(
                N_("No matching tracks for file '%(filename)s'"),
                {'filename': self.filename},
                timeout=3000
            )
            self.clear_pending()
            return

        # multiple matches -- calculate similarities to each of them
        match = sorted((self.metadata.compare_to_track(
            track, self.comparison_weights) for track in tracks),
            reverse=True, key=itemgetter(0))[0]
        if lookuptype != 'acoustid' and match[0] < config.setting['file_lookup_threshold']:
            self.tagger.window.set_statusbar_message(
                N_("No matching tracks above the threshold for file '%(filename)s'"),
                {'filename': self.filename},
                timeout=3000
            )
            self.clear_pending()
            return

        self.tagger.window.set_statusbar_message(
            N_("File '%(filename)s' identified!"),
            {'filename': self.filename},
            timeout=3000
        )

        self.clear_pending()

        rg, release, track = match[1:]
        if lookuptype == 'acoustid':
            self.tagger.acoustidmanager.add(self, track['id'])
        if release:
            self.tagger.get_release_group_by_id(rg['id']).loaded_albums.add(release['id'])
            self.tagger.move_file_to_track(self, release['id'], track['id'])
        else:
            node = track if 'title' in track else None
            self.tagger.move_file_to_nat(self, track['id'], node=node)
Пример #24
0
    def _process_reply(self, reply):
        try:
            request, handler, xml, refresh = self._active_requests.pop(reply)
        except KeyError:
            log.error("Error: Request not found for %s" % str(reply.request().url().toString()))
            return
        error = int(reply.error())
        redirect = reply.attribute(QtNetwork.QNetworkRequest.RedirectionTargetAttribute).toUrl()
        fromCache = reply.attribute(QtNetwork.QNetworkRequest.SourceIsFromCacheAttribute).toBool()
        cached = ' (CACHED)' if fromCache else ''
        log.debug("Received reply for %s: HTTP %d (%s) %s",
                  reply.request().url().toString(),
                  reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute).toInt()[0],
                  reply.attribute(QtNetwork.QNetworkRequest.HttpReasonPhraseAttribute).toString(),
                  cached
                  )
        if handler is not None:
            if error:
                log.error("Network request error for %s: %s (QT code %d, HTTP code %d)",
                          reply.request().url().toString(),
                          reply.errorString(),
                          error,
                          reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute).toInt()[0])

            # Redirect if found and not infinite
            if not redirect.isEmpty() and not XmlWebService.urls_equivalent(redirect, reply.request().url()):
                log.debug("Redirect to %s requested", redirect.toString())
                redirect_host = str(redirect.host())
                redirect_port = redirect.port(80)

                url = request.url()
                original_host = str(url.host())
                original_port = url.port(80)

                if ((original_host, original_port) in REQUEST_DELAY
                    and (redirect_host, redirect_port) not in REQUEST_DELAY):
                    log.debug("Setting rate limit for %s:%i to %i" %
                              (redirect_host, redirect_port,
                               REQUEST_DELAY[(original_host, original_port)]))
                    REQUEST_DELAY[(redirect_host, redirect_port)] =\
                        REQUEST_DELAY[(original_host, original_port)]

                self.get(redirect_host,
                         redirect_port,
                         # retain path, query string and anchors from redirect URL
                         redirect.toString(QUrl.RemoveAuthority | QUrl.RemoveScheme),
                         handler, xml, priority=True, important=True, refresh=refresh,
                         cacheloadcontrol=request.attribute(QtNetwork.QNetworkRequest.CacheLoadControlAttribute))
            elif xml:
                document = _read_xml(QXmlStreamReader(reply))
                handler(document, reply, error)
            else:
                handler(str(reply.readAll()), reply, error)
        reply.close()
        self.num_pending_web_requests -= 1
        self.tagger.tagger_stats_changed.emit()
def _process_discogs_relation(dcurl):
#
# Once this function is called with the discogs url, it will get the discogs image url
#
	log.error ("discogs relation is: %s", dcurl)

	slashindex = dcurl.rfind('/')
	dcrelease = dcurl[slashindex+1:len(dcurl)]

	_process_discogs_release(dcrelease)
Пример #26
0
 def run_item(self, item):
     func, next, priority = item
     try:
         result = func()
     except:
         from picard import log
         log.error(traceback.format_exc())
         self.to_main(next, priority, error=sys.exc_info()[1])
     else:
         self.to_main(next, priority, result=result)
Пример #27
0
 def __load_keys(self):
     for key in self.__qt_keys():
         try:
             self.__config[key] = self.__qt_config.value(key)
         except TypeError:
             # Related to PICARD-1255, Unable to load the object into
             # Python at all. Something weird with the way it is read and converted
             # via the Qt C++ API. Simply ignore the key and it will be reset to
             # default whenever the user opens Picard options
             log.error('Unable to load config value: %s', key)
Пример #28
0
 def _recording_request_finished(self, recording, http, error):
     if error:
         log.error("%r", http.errorString())
         return
     try:
         self._parse_recording(recording)
         for file in self.linked_files:
             self.update_file_metadata(file)
     except Exception:
         log.error(traceback.format_exc())
Пример #29
0
 def _marked_for_update(self):
     for file in os.listdir(self.plugins_directory):
         if file.endswith(_UPDATE_SUFFIX):
             source_path = os.path.join(self.plugins_directory, file)
             target_path = strip_update_suffix(source_path)
             plugin_name = _plugin_name_from_path(target_path)
             if plugin_name:
                 yield (source_path, target_path, plugin_name)
             else:
                 log.error('Cannot get plugin name from %r', source_path)
Пример #30
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = self._File(encode_filename(filename))
        metadata = Metadata()
        if file.tags:
            for origname, values in file.tags.items():
                if origname.lower().startswith("cover art") and values.kind == mutagen.apev2.BINARY:
                    if '\0' in values.value:
                        descr, data = values.value.split('\0', 1)
                        try:
                            coverartimage = TagCoverArtImage(
                                file=filename,
                                tag=origname,
                                data=data,
                            )
                        except CoverArtImageError as e:
                            log.error('Cannot load image from %r: %s' %
                                      (filename, e))
                        else:
                            metadata.append_image(coverartimage)

                # skip EXTERNAL and BINARY values
                if values.kind != mutagen.apev2.TEXT:
                    continue
                for value in values:
                    name = origname
                    if name == "Year":
                        name = "date"
                        value = sanitize_date(value)
                    elif name == "Track":
                        name = "tracknumber"
                        track = value.split("/")
                        if len(track) > 1:
                            metadata["totaltracks"] = track[1]
                            value = track[0]
                    elif name == "Disc":
                        name = "discnumber"
                        disc = value.split("/")
                        if len(disc) > 1:
                            metadata["totaldiscs"] = disc[1]
                            value = disc[0]
                    elif name == 'Performer' or name == 'Comment':
                        name = name.lower() + ':'
                        if value.endswith(')'):
                            start = value.rfind(' (')
                            if start > 0:
                                name += value[start + 2:-1]
                                value = value[:start]
                    elif name in self.__translate:
                        name = self.__translate[name]
                    else:
                        name = name.lower()
                    metadata.add(name, value)
        self._info(metadata, file)
        return metadata
Пример #31
0
 def _display_results(self):
     # Display results to user.
     key = ''
     high_version = PICARD_VERSION
     for test_key in PROGRAM_UPDATE_LEVELS:
         update_level = PROGRAM_UPDATE_LEVELS[test_key]['name']
         version_tuple = self._available_versions.get(update_level, {}).get(
             'version', (0, 0, 0, ''))
         try:
             test_version = Version(*version_tuple)
         except (TypeError, VersionError):
             log.error('Invalid version %r for update level %s.' %
                       (version_tuple, update_level))
             continue
         if self._update_level >= test_key and test_version > high_version:
             key = PROGRAM_UPDATE_LEVELS[test_key]['name']
             high_version = test_version
     if key:
         if QMessageBox.information(
                 self._parent, _("Picard Update"),
                 _("A new version of Picard is available.\n\n"
                   "This version: {picard_old_version}\n"
                   "New version: {picard_new_version}\n\n"
                   "Would you like to download the new version?").format(
                       picard_old_version=PICARD_FANCY_VERSION_STR,
                       picard_new_version=self._available_versions[key]
                       ['tag']), QMessageBox.Ok | QMessageBox.Cancel,
                 QMessageBox.Cancel) == QMessageBox.Ok:
             webbrowser2.open(
                 self._available_versions[key]['urls']['download'])
     else:
         if self._show_always:
             if self._update_level in PROGRAM_UPDATE_LEVELS:
                 update_level = PROGRAM_UPDATE_LEVELS[
                     self._update_level]['title']
             else:
                 update_level = N_('unknown')
             QMessageBox.information(
                 self._parent, _("Picard Update"),
                 _("There is no update currently available for your subscribed update level: {update_level}\n\n"
                   "Your version: {picard_old_version}\n").format(
                       update_level=_(update_level),
                       picard_old_version=PICARD_FANCY_VERSION_STR,
                   ), QMessageBox.Ok, QMessageBox.Ok)
Пример #32
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)
Пример #33
0
    def _lookup_finished(self, lookuptype, document, http, error):
        self.lookup_task = None

        if self.state == File.REMOVED:
            return
        if error:
            log.error("Network error encountered during the lookup for %s. Error code: %s",
                      self.filename, error)
        try:
            tracks = document['recordings']
        except (KeyError, TypeError):
            tracks = None

        def statusbar(message):
            self.tagger.window.set_statusbar_message(
                message,
                {'filename': self.filename},
                timeout=3000
            )

        if tracks:
            if lookuptype == File.LOOKUP_ACOUSTID:
                threshold = 0
            else:
                threshold = config.setting['file_lookup_threshold']

            trackmatch = self._match_to_track(tracks, threshold=threshold)
            if trackmatch is None:
                statusbar(N_("No matching tracks above the threshold for file '%(filename)s'"))
            else:
                statusbar(N_("File '%(filename)s' identified!"))
                (track_id, release_group_id, release_id, node) = trackmatch
                if lookuptype == File.LOOKUP_ACOUSTID:
                    self.tagger.acoustidmanager.add(self, track_id)
                if release_group_id is not None:
                    releasegroup = self.tagger.get_release_group_by_id(release_group_id)
                    releasegroup.loaded_albums.add(release_id)
                    self.tagger.move_file_to_track(self, release_id, track_id)
                else:
                    self.tagger.move_file_to_nat(self, track_id, node=node)
        else:
            statusbar(N_("No matching tracks for file '%(filename)s'"))

        self.clear_pending()
Пример #34
0
    def _process_request(self):
        conn = self.sender()
        rawline = conn.readLine().data()
        log.debug("Browser integration request: %r", rawline)

        def parse_line(line):
            orig_line = line
            try:
                line = line.split()
                if line[0] == "GET" and "?" in line[1]:
                    parsed = urlparse(line[1])
                    args = parse_qs(parsed.query)
                    if 'id' in args and args['id']:
                        mbid = args['id'][0]
                        if not mbid_validate(mbid):
                            log.error("Browser integration failed: bad mbid %r", mbid)
                            return False
                        def load_it(loader):
                            self.tagger.bring_tagger_front()
                            loader(mbid)
                            return True
                        action = parsed.path
                        if action == '/openalbum':
                            return load_it(self.tagger.load_album)
                        elif action == '/opennat':
                            return load_it(self.tagger.load_nat)
            except Exception as e:
                log.error("Browser integration failed with %r on line %r", e, orig_line)
                return False
            log.error("Browser integration failed: cannot parse %r", orig_line)
            return False

        try:
            line = rawline.decode()
            if parse_line(line):
                conn.write(response(200))
            else:
                conn.write(response(400))
        except UnicodeDecodeError as e:
            conn.write(response(500))
            log.error(e)
            return
        finally:
            conn.disconnectFromHost()
Пример #35
0
    def _load(self, filename):
        log.debug("Loading file %r", filename)
        file = ASF(encode_filename(filename))
        metadata = Metadata()
        for name, values in file.tags.items():
            if name == 'WM/Picture':
                for image in values:
                    (mime, data, type_,
                     description) = unpack_image(image.value)
                    try:
                        coverartimage = TagCoverArtImage(
                            file=filename,
                            tag=name,
                            types=types_from_id3(type_),
                            comment=description,
                            support_types=True,
                            data=data,
                        )
                    except CoverArtImageError as e:
                        log.error('Cannot load image from %r: %s' %
                                  (filename, e))
                    else:
                        metadata.append_image(coverartimage)

                continue
            elif name not in self.__RTRANS:
                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(string_(values[0])) / 99.0 *
                        (config.setting['rating_steps'] - 1)))
            elif name == 'WM/PartOfSet':
                disc = string_(values[0]).split("/")
                if len(disc) > 1:
                    metadata["totaldiscs"] = disc[1]
                    values[0] = disc[0]
            name = self.__RTRANS[name]
            values = [string_(value) for value in values if value]
            if values:
                metadata[name] = values
        self._info(metadata, file)
        return metadata
Пример #36
0
def _tags_downloaded(album, metadata, min_usage, ignore, next_, current, data,
                     reply, error):
    if error:
        album._requests -= 1
        album._finalize_loading(None)
        return

    try:
        try:
            intags = data.lfm[0].toptags[0].tag
        except AttributeError:
            intags = []
        tags = []
        for tag in intags:
            name = tag.name[0].text.strip()
            try:
                count = int(tag.count[0].text.strip())
            except ValueError:
                count = 0
            if count < min_usage:
                break
            try:
                name = TRANSLATE_TAGS[name]
            except KeyError:
                pass
            if not matches_ignored(ignore, name):
                tags.append(name.title())
        url = reply.url().toString()
        _cache[url] = tags
        _tags_finalize(album, metadata, current + tags, next_)

        # Process any pending requests for the same URL
        if url in _pending_requests:
            pending = _pending_requests[url]
            del _pending_requests[url]
            for delayed_call in pending:
                delayed_call()

    except Exception:
        log.error('Problem processing download tags', exc_info=True)
    finally:
        album._requests -= 1
        album._finalize_loading(None)
Пример #37
0
 def _parse_recording(self, recording):
     recording_to_metadata(recording, self)
     self._customize_metadata()
     m = self.metadata
     run_track_metadata_processors(self.album, m, None, recording)
     if config.setting["enable_tagger_script"]:
         script = config.setting["tagger_script"]
         if script:
             parser = ScriptParser()
             try:
                 parser.eval(script, m)
             except:
                 log.error(traceback.format_exc())
             m.strip_whitespace()
     self.loaded = True
     if self.callback:
         self.callback()
         self.callback = None
     self.tagger.nats.update(True)
Пример #38
0
    def _clustering_finished(self, callback, result=None, error=None):
        if error:
            log.error('Error while clustering: %r', error)
            return

        with self.window.ignore_selection_changes:
            self.window.set_sorting(False)
            for file_cluster in process_events_iter(result):
                files = set(file_cluster.files)
                if len(files) > 1:
                    cluster = self.load_cluster(file_cluster.title,
                                                file_cluster.artist)
                else:
                    cluster = self.unclustered_files
                cluster.add_files(files)
            self.window.set_sorting(True)

        if callback:
            callback()
Пример #39
0
def result(album, metadata, data, reply, error):
    moods = []
    genres = []
    try:
        data = load_json(data)["highlevel"]
        for k, v in data.items():
            if k.startswith("genre_") and not v["value"].startswith("not_"):
                genres.append(v["value"])
            if k.startswith("mood_") and not v["value"].startswith("not_"):
                moods.append(v["value"])

        metadata["genre"] = genres
        metadata["mood"] = moods
        log.debug("%s: Track %s (%s) Parsed response (genres: %s, moods: %s)", PLUGIN_NAME, metadata["musicbrainz_recordingid"], metadata["title"], str(genres), str(moods))
    except Exception as e:
        log.error("%s: Track %s (%s) Error parsing response: %s", PLUGIN_NAME, metadata["musicbrainz_recordingid"], metadata["title"], str(e))
    finally:
        album._requests -= 1
        album._finalize_loading(None)
Пример #40
0
 def on_refresh_access_token_finished(self, callback, data, http, error):
     access_token = None
     try:
         if error:
             log.error("OAuth: access_token refresh failed: %s", data)
             if http.attribute(
                     QNetworkRequest.HttpStatusCodeAttribute) == 400:
                 response = load_json(data)
                 if response["error"] == "invalid_grant":
                     self.forget_refresh_token()
         else:
             access_token = data["access_token"]
             self.set_access_token(access_token, data["expires_in"])
     except Exception as e:
         log.error(
             'OAuth: Unexpected error handling access token response: %r',
             e)
     finally:
         callback(access_token=access_token)
Пример #41
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)
Пример #42
0
 def add_files(self, filenames, target=None):
     """Add files to the tagger."""
     ignoreregex = None
     pattern = config.setting['ignore_regex']
     if pattern:
         try:
             ignoreregex = re.compile(pattern)
         except re.error as e:
             log.error("Failed evaluating regular expression for ignore_regex: %s", e)
     ignore_hidden = config.setting["ignore_hidden_files"]
     new_files = []
     for filename in filenames:
         filename = normpath(filename)
         if ignore_hidden and is_hidden(filename):
             log.debug("File ignored (hidden): %r" % (filename))
             continue
         # Ignore .smbdelete* files which Applie iOS SMB creates by renaming a file when it cannot delete it
         if os.path.basename(filename).startswith(".smbdelete"):
             log.debug("File ignored (.smbdelete): %r", filename)
             continue
         if ignoreregex is not None and ignoreregex.search(filename):
             log.info("File ignored (matching %r): %r" % (pattern, filename))
             continue
         if filename not in self.files:
             file = open_file(filename)
             if file:
                 self.files[filename] = file
                 new_files.append(file)
             QtCore.QCoreApplication.processEvents()
     if new_files:
         log.debug("Adding files %r", new_files)
         new_files.sort(key=lambda x: x.filename)
         self.window.set_sorting(False)
         self._pending_files_count += len(new_files)
         for i, file in enumerate(new_files):
             file.load(partial(self._file_loaded, target=target))
             # Calling processEvents helps processing the _file_loaded
             # callbacks in between, which keeps the UI more responsive.
             # Avoid calling it to often to not slow down the loading to much
             # Using an uneven number to have the unclustered file counter
             # not look stuck in certain digits.
             if i % 17 == 0:
                 QtCore.QCoreApplication.processEvents()
Пример #43
0
    def value(self, name, option_type, default=None):
        """Return an option value converted to the given Option type."""
        if name in self:
            key = self.key(name)
            memovar = self._memoization[key]

            if memovar.dirty:
                try:
                    value = self.raw_value(name, qtype=option_type.qtype)
                    value = option_type.convert(value)
                    memovar.dirty = False
                    memovar.value = value
                except Exception as why:
                    log.error('Cannot read %s value: %s', self.key(name), why, exc_info=True)
                    value = default
                return value
            else:
                return memovar.value
        return default
 def get_data(self, album, track_metadata, trackXmlNode, releaseXmlNode):
     if "musicbrainz_recordingid" not in track_metadata:
         log.error("%s: Error parsing response. No MusicBrainz recording id found.",
                   PLUGIN_NAME)
         return
     recordingId = track_metadata['musicbrainz_recordingid']
     if recordingId:
         log.debug("%s: Add AcousticBrainz request for %s (%s)",
                   PLUGIN_NAME, track_metadata['title'], recordingId)
         self.album_add_request(album)
         path = "/%s/low-level" % recordingId
         return album.tagger.webservice.get(
                     ACOUSTICBRAINZ_HOST,
                     ACOUSTICBRAINZ_PORT,
                     path,
                     partial(self.process_data, album, track_metadata),
                     priority=True,
                     important=False,
                     parse_response_type=None)
Пример #45
0
    def _batch_submit(self, submissions, errors=None):
        if not submissions:  # All fingerprints submitted, nothing to do
            if errors:
                log_msg = N_(
                    "AcoustID submission finished, but not all fingerprints have been submitted"
                )
            else:
                log_msg = N_("AcoustID submission finished successfully")
            log.debug(log_msg)
            self.tagger.window.set_statusbar_message(log_msg,
                                                     echo=None,
                                                     timeout=3000)
            self._check_unsubmitted()
            return

        batch, submissions = self._batch(submissions)

        if not batch:
            if self.max_batch_size == 0:
                log_msg = N_(
                    "AcoustID submission failed permanently, maximum batch size reduced to zero"
                )
            else:
                log_msg = N_(
                    "AcoustID submission failed permanently, probably too many retries"
                )
            log.error(log_msg)
            self.tagger.window.set_statusbar_message(log_msg,
                                                     echo=None,
                                                     timeout=3000)
            self._check_unsubmitted()
            return

        log.debug(
            "AcoustID: submitting batch of %d fingerprints (%d remaining)...",
            len(batch), len(submissions))
        self.tagger.window.set_statusbar_message(
            N_('Submitting AcoustIDs ...'), echo=None)
        if not errors:
            errors = []
        self._acoustid_api.submit_acoustid_fingerprints(
            [submission for file_, submission in batch],
            partial(self._batch_submit_finished, submissions, batch, errors))
Пример #46
0
def open_(filename):
    """Open the specified file and return a File instance with the appropriate format handler, or None."""
    try:
        # First try to guess the format on the basis of file headers
        audio_file = guess_format(filename)
        if not audio_file:
            i = filename.rfind(".")
            if i < 0:
                return None
            ext = filename[i + 1:].lower()
            # Switch to extension based opening if guess_format fails
            audio_file = _extensions[ext](filename)
        return audio_file
    except KeyError:
        # None is returned if both the methods fail
        return None
    except Exception as error:
        log.error("Error occurred:\n{}".format(error))
        return None
Пример #47
0
 def _loading_finished(self, callback, result=None, error=None):
     if self.state != File.PENDING or self.tagger.stopping:
         return
     if error is not None:
         self.error = str(error)
         self.state = self.ERROR
         from picard.formats import supported_extensions
         file_name, file_extension = os.path.splitext(self.base_filename)
         if file_extension not in supported_extensions():
             self.remove()
             log.error('Unsupported media file %r wrongly loaded. Removing ...', self)
             return
     else:
         self.error = None
         self.state = self.NORMAL
         self._copy_loaded_metadata(result)
     run_file_post_load_processors(self)
     self.update()
     callback(self)
Пример #48
0
 def callback(self, cluster_list):
     extra = config.setting[KEY_EXTRA].split()
     for cluster in cluster_list:
         if isinstance(cluster, Cluster):
             parts = []
             if 'albumartist' in cluster.metadata and cluster.metadata['albumartist']:
                 parts.extend(cluster.metadata['albumartist'].split())
             if 'album' in cluster.metadata and cluster.metadata['albumartist']:
                 parts.extend(cluster.metadata['album'].split())
             if parts:
                 if extra:
                     parts.extend(extra)
                 text = ' '.join(parts)
                 do_lookup(text)
             else:
                 lookup_error()
         else:
             log.error("{0}: Argument is not a cluster. {1}".format(PLUGIN_NAME, cluster,))
             show_popup(_('Lookup Error'), _('There was a problem with the information provided for the cluster.'))
Пример #49
0
    def _handle_redirect(self, reply, request, redirect):
        url = request.url()
        error = int(reply.error())
        # merge with base url (to cover the possibility of the URL being relative)
        redirect = url.resolved(redirect)
        if not WebService.urls_equivalent(redirect, reply.request().url()):
            log.debug("Redirect to %s requested",
                      redirect.toString(QUrl.RemoveUserInfo))
            redirect_host = string_(redirect.host())
            redirect_port = self.url_port(redirect)
            redirect_query = dict(
                QUrlQuery(redirect).queryItems(QUrl.FullyEncoded))
            redirect_path = redirect.path()

            original_host = string_(url.host())
            original_port = self.url_port(url)
            original_host_key = (original_host, original_port)
            redirect_host_key = (redirect_host, redirect_port)
            if (original_host_key in REQUEST_DELAY_MINIMUM
                    and redirect_host_key not in REQUEST_DELAY_MINIMUM):
                log.debug("Setting the minimum rate limit for %s to %i" %
                          (redirect_host_key,
                           REQUEST_DELAY_MINIMUM[original_host_key]))
                REQUEST_DELAY_MINIMUM[
                    redirect_host_key] = REQUEST_DELAY_MINIMUM[
                        original_host_key]

            self.get(redirect_host,
                     redirect_port,
                     redirect_path,
                     request.handler,
                     request.parse_response_type,
                     priority=True,
                     important=True,
                     refresh=request.refresh,
                     queryargs=redirect_query,
                     cacheloadcontrol=request.attribute(
                         QtNetwork.QNetworkRequest.CacheLoadControlAttribute))
        else:
            log.error("Redirect loop: %s",
                      reply.request().url().toString(QUrl.RemoveUserInfo))
            handler(reply.readAll(), reply, error)
Пример #50
0
 def add_files(self, filenames, target=None, result=None):
     """Add files to the tagger."""
     if result:
         filenames = result  # Handles add_directory task results coming from a worker thread
     ignoreregex = None
     pattern = config.setting['ignore_regex']
     if pattern:
         try:
             ignoreregex = re.compile(pattern)
         except re.error as e:
             log.error(
                 "Failed evaluating regular expression for ignore_regex: %s",
                 e)
     ignore_hidden = config.setting["ignore_hidden_files"]
     new_files = []
     for filename in filenames:
         filename = os.path.normpath(os.path.realpath(filename))
         if ignore_hidden and is_hidden(filename):
             log.debug("File ignored (hidden): %r" % (filename))
             continue
         # Ignore .smbdelete* files which Applie iOS SMB creates by renaming a file when it cannot delete it
         if os.path.basename(filename).startswith(".smbdelete"):
             log.debug("File ignored (.smbdelete): %r", filename)
             continue
         if ignoreregex is not None and ignoreregex.search(filename):
             log.info("File ignored (matching %r): %r" %
                      (pattern, filename))
             continue
         if filename not in self.files:
             file = open_file(filename)
             if file:
                 self.files[filename] = file
                 new_files.append(file)
             QtCore.QCoreApplication.processEvents()
     if new_files:
         log.debug("Adding files %r", new_files)
         new_files.sort(key=lambda x: x.filename)
         self.window.set_sorting(False)
         self._pending_files_count += len(new_files)
         for file in new_files:
             file.load(partial(self._file_loaded, target=target))
             QtCore.QCoreApplication.processEvents()
Пример #51
0
    def _move_additional_files(self, old_filename, new_filename):
        """Move extra files, like images, playlists..."""
        patterns = config.setting["move_additional_files_pattern"]
        pattern_regexes = set()
        for pattern in patterns.split():
            pattern = pattern.strip()
            if not pattern:
                continue
            pattern_regex = re.compile(fnmatch.translate(pattern), re.IGNORECASE)
            match_hidden = pattern.startswith('.')
            pattern_regexes.add((pattern_regex, match_hidden))
        if not pattern_regexes:
            return
        new_path = os.path.dirname(new_filename)
        old_path = os.path.dirname(old_filename)
        moves = set()
        try:
            # TODO: use with statement with python 3.6+
            for entry in os.scandir(old_path):
                is_hidden = entry.name.startswith('.')
                for pattern_regex, match_hidden in pattern_regexes:
                    if is_hidden and not match_hidden:
                        continue
                    if pattern_regex.match(entry.name):
                        new_file_path = os.path.join(new_path, entry.name)
                        moves.add((entry.path, new_file_path))
                        break  # we are done with this file
        except OSError as why:
            log.error("Failed to scan %r: %s", old_path, why)
            return

        for old_file_path, new_file_path in moves:
            # FIXME we shouldn't do this from a thread!
            if self.tagger.files.get(decode_filename(old_file_path)):
                log.debug("File loaded in the tagger, not moving %r", old_file_path)
                continue
            log.debug("Moving %r to %r", old_file_path, new_file_path)
            try:
                shutil.move(old_file_path, new_file_path)
            except OSError as why:
                log.error("Failed to move %r to %r: %s", old_file_path,
                          new_file_path, why)
Пример #52
0
    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)
Пример #53
0
 def _process_request(self):
     conn = self.sender()
     line = str(conn.readLine())
     conn.write(
         "HTTP/1.1 200 OK\r\nCache-Control: max-age=0\r\n\r\nNothing to see here."
     )
     conn.disconnectFromHost()
     line = line.split()
     log.debug("Browser integration request: %r", line)
     if line[0] == "GET" and "?" in line[1]:
         action, args = line[1].split("?")
         args = [a.split("=", 1) for a in args.split("&")]
         args = dict((a, unicode(QtCore.QUrl.fromPercentEncoding(b)))
                     for (a, b) in args)
         if action == "/openalbum":
             self.tagger.load_album(args["id"])
         elif action == "/opennat":
             self.tagger.load_nat(args["id"])
         else:
             log.error("Unknown browser integration request: %r", action)
Пример #54
0
    def _cover_downloaded(self, row, 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 = self.table.cellWidget(row, len(self.table_headers) - 1)

        if error:
            cover_cell.not_found()
        else:
            pixmap = QtGui.QPixmap()
            try:
                pixmap.loadFromData(data)
                cover_cell.update(pixmap)
            except Exception as e:
                cover_cell.not_found()
                log.error(e)
Пример #55
0
def open_(filename):
    """Open the specified file and return a File instance with the appropriate format handler, or None."""
    try:
        # Use extension based opening as default
        i = filename.rfind(".")
        if i >= 0:
            ext = filename[i+1:].lower()
            audio_file = _extensions[ext](filename)
        else:
            # If there is no extension, try to guess the format based on file headers
            audio_file = guess_format(filename)
        if not audio_file:
            return None
        return audio_file
    except KeyError:
        # None is returned if both the methods fail
        return None
    except Exception as error:
        log.error("Error occurred:\n{}".format(error))
        return None
Пример #56
0
 def website_process(self, artistId, response, reply, error):
     if error:
         log.error("%s: %r: Network error retrieving artist record", PLUGIN_NAME, artistId)
         tuples = self.website_queue.remove(artistId)
         for track, album in tuples:
             self.album_remove_request(album)
         return
     urls = self.artist_process_metadata(artistId, response)
     self.website_cache[artistId] = urls
     tuples = self.website_queue.remove(artistId)
     log.debug("%s: %r: Artist Official Homepages = %r", PLUGIN_NAME,
               artistId, urls)
     for track, album in tuples:
         self.album_remove_request(album)
         if urls:
             tm = track.metadata
             tm['website'] = urls
             for file in track.iterfiles(True):
                 fm = file.metadata
                 fm['website'] = urls
Пример #57
0
 def __fingerprint_submission_finished(self, fingerprints, document, http,
                                       error):
     if error:
         mparms = {'error': unicode(http.errorString())}
         log.error("AcoustID: submission failed with error '%(error)s'" %
                   mparms)
         self.tagger.window.set_statusbar_message(
             N_("AcoustID submission failed with error '%(error)s'"),
             mparms,
             echo=None,
             timeout=3000)
     else:
         log.debug('AcoustID: successfully submitted')
         self.tagger.window.set_statusbar_message(
             N_('AcoustIDs successfully submitted.'),
             echo=None,
             timeout=3000)
         for submission in fingerprints:
             submission.orig_recordingid = submission.recordingid
         self._check_unsubmitted()
Пример #58
0
def get_file_naming_script(settings):
    """Retrieve the file naming script.

    Args:
        settings (ConfigSection): Object containing the user settings

    Returns:
        str: The text of the file naming script if available, otherwise None
    """
    from picard.script import get_file_naming_script_presets
    scripts = settings["file_renaming_scripts"]
    selected_id = settings["selected_file_naming_script_id"]
    if selected_id:
        if scripts and selected_id in scripts:
            return scripts[selected_id]["script"]
        for item in get_file_naming_script_presets():
            if item["id"] == selected_id:
                return str(item["script"])
    log.error("Unable to retrieve the file naming script '%s'", selected_id)
    return None
Пример #59
0
 def on_exchange_authorization_code_finished(self, scopes, callback, data,
                                             http, error):
     successful = False
     error_msg = None
     try:
         if error:
             log.error("OAuth: authorization_code exchange failed: %s",
                       data)
             error_msg = self._extract_error_description(http, data)
         else:
             self.set_refresh_token(data["refresh_token"], scopes)
             self.set_access_token(data["access_token"], data["expires_in"])
             successful = True
     except Exception as e:
         log.error(
             'OAuth: Unexpected error handling authorization code response: %r',
             e)
         error_msg = _('Unexpected authentication error')
     finally:
         callback(successful=successful, error_msg=error_msg)
Пример #60
0
    def _init_headers(self, high_prio_no_cache=False):
        self.setHeader(QNetworkRequest.UserAgentHeader, USER_AGENT_STRING)

        if self.mblogin or high_prio_no_cache:
            self.setPriority(QNetworkRequest.HighPriority)
            self.setAttribute(QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.AlwaysNetwork)
        elif self.cacheloadcontrol is not None:
            self.setAttribute(QNetworkRequest.CacheLoadControlAttribute, self.cacheloadcontrol)

        if self.parse_response_type:
            try:
                self.response_mimetype = WebService.get_response_mimetype(self.parse_response_type)
                self.response_parser = WebService.get_response_parser(self.parse_response_type)
            except UnknownResponseParserError as e:
                log.error(e.args[0])
            else:
                self.setRawHeader(b"Accept", self.response_mimetype.encode('utf-8'))

        if self.data:
                self.setHeader(QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded")