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)
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)
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)
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)
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)
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()
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)
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)
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)
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)
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
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)
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)
def _caa_json_downloaded(self, cover_cell, data, http, error): """Handle json reply from CAA server. If server replies without error, try to get small thumbnail of front coverart of the release. """ if not self.table: return cover_cell.fetch_task = None if error: cover_cell.not_found() return front = None try: for image in data["images"]: if image["front"]: front = image break if front: url = front["thumbnails"]["small"] coverartimage = CaaThumbnailCoverArtImage(url=url) cover_cell.fetch_task = self.tagger.webservice.download( coverartimage.host, coverartimage.port, coverartimage.path, partial(self._cover_downloaded, cover_cell) ) else: cover_cell.not_found() except (AttributeError, KeyError, TypeError): log.error("Error reading CAA response", exc_info=True) cover_cell.not_found()
def 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)
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()
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)
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)
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
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()
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
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)
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)
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)
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)
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)
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())
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)
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
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)
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)
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()
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()
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
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)
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)
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()
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)
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)
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)
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()
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)
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))
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
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)
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.'))
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)
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()
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)
def _cover_downloaded(self, cover_cell, data, http, error): """Handle cover art query reply from CAA server. If server returns the cover image successfully, update the cover art cell of particular release. Args: row -- Album's row in results table """ cover_cell.fetch_task = None if error: cover_cell.not_found() else: pixmap = QtGui.QPixmap() try: pixmap.loadFromData(data) cover_cell.set_pixmap(pixmap) except Exception as e: cover_cell.not_found() log.error(e)
def _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)
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)
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
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
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()
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
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)
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")