def incdec_number(url, incdec, count=1, segments=None): """Find a number in the url and increment or decrement it. Args: url: The current url incdec: Either 'increment' or 'decrement' count: The number to increment or decrement by segments: A set of URL segments to search. Valid segments are: 'host', 'port', 'path', 'query', 'anchor'. Default: {'path', 'query'} Return: The new url with the number incremented/decremented. Raises IncDecError if the url contains no number. """ if not url.isValid(): raise InvalidUrlError(url) if segments is None: segments = {'path', 'query'} valid_segments = {'host', 'port', 'path', 'query', 'anchor'} if segments - valid_segments: extra_elements = segments - valid_segments raise IncDecError("Invalid segments: {}".format( ', '.join(extra_elements)), url) # Make a copy of the QUrl so we don't modify the original url = QUrl(url) # Order as they appear in a URL segment_modifiers = [ ('host', url.host, url.setHost), ('port', lambda: str(url.port()) if url.port() > 0 else '', lambda x: url.setPort(int(x))), ('path', url.path, url.setPath), ('query', url.query, url.setQuery), ('anchor', url.fragment, url.setFragment), ] # We're searching the last number so we walk the url segments backwards for segment, getter, setter in reversed(segment_modifiers): if segment not in segments: continue # Get the last number in a string match = re.fullmatch(r'(.*\D|^)(0*)(\d+)(.*)', getter()) if not match: continue setter(_get_incdec_value(match, incdec, url, count)) return url raise IncDecError("No number found in URL!", url)
def transform(self, value): if not value: return None elif value == 'system': return SYSTEM_PROXY elif value == 'none': return QNetworkProxy(QNetworkProxy.NoProxy) url = QUrl(value) typ = self.PROXY_TYPES[url.scheme()] proxy = QNetworkProxy(typ, url.host()) if url.port() != -1: proxy.setPort(url.port()) if url.userName(): proxy.setUser(url.userName()) if url.password(): proxy.setPassword(url.password()) return proxy
def __init__(self, connections=[], parent=None): """ Constructor @param connections list of database connections to add (list of strings) @param parent reference to the parent widget (QWidget) """ super(SqlBrowser, self).__init__(parent) self.setObjectName("SqlBrowser") self.setWindowTitle(self.tr("SQL Browser")) self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) from .SqlBrowserWidget import SqlBrowserWidget self.__browser = SqlBrowserWidget(self) self.setCentralWidget(self.__browser) self.__browser.statusMessage.connect(self.statusBar().showMessage) self.__initActions() self.__initMenus() self.__initToolbars() self.resize(self.__browser.size()) self.__warnings = [] for connection in connections: url = QUrl(connection, QUrl.TolerantMode) if not url.isValid(): self.__warnings.append( self.tr("Invalid URL: {0}").format(connection)) continue err = self.__browser.addConnection(url.scheme(), url.path(), url.userName(), url.password(), url.host(), url.port(-1)) if err.type() != QSqlError.NoError: self.__warnings.append( self.tr("Unable to open connection: {0}".format( err.text()))) QTimer.singleShot(0, self.__uiStartUp)
def matches(self, qurl: QUrl) -> bool: """Check if the pattern matches the given QUrl.""" qtutils.ensure_valid(qurl) if self._match_all: return True if not self._matches_scheme(qurl.scheme()): return False # FIXME ignore for file:// like Chromium? if not self._matches_host(qurl.host()): return False if not self._matches_port(qurl.scheme(), qurl.port()): return False if not self._matches_path(qurl.path()): return False return True
def host_tuple(url: QUrl) -> HostTupleType: """Get a (scheme, host, port) tuple from a QUrl. This is suitable to identify a connection, e.g. for SSL errors. """ ensure_valid(url) scheme, host, port = url.scheme(), url.host(), url.port() assert scheme if not host: raise ValueError("Got URL {} without host.".format( url.toDisplayString())) if port == -1: port_mapping = { 'http': 80, 'https': 443, 'ftp': 21, } try: port = port_mapping[scheme] except KeyError: raise ValueError("Got URL {} with unknown port.".format( url.toDisplayString())) return scheme, host, port
def queryProxy(self, query): """ Public method to determine a proxy for a given query. @param query reference to the query object (QNetworkProxyQuery) @return list of proxies in order of preference (list of QNetworkProxy) """ if query.queryType() == QNetworkProxyQuery.UrlRequest and \ query.protocolTag() in ["http", "https", "ftp"]: # use proxy at all ? if not Preferences.getUI("UseProxy"): return [QNetworkProxy(QNetworkProxy.NoProxy)] # test for exceptions exceptions = Preferences.getUI("ProxyExceptions") if exceptions != self.__exceptions: self.__setExceptions(exceptions) urlHost = query.url().host() for matcher in self.__hostnameMatchers: if matcher.match(urlHost): return [QNetworkProxy(QNetworkProxy.NoProxy)] # determine proxy if Preferences.getUI("UseSystemProxy"): proxyList = QNetworkProxyFactory.systemProxyForQuery(query) if not Globals.isWindowsPlatform() and \ len(proxyList) == 1 and \ proxyList[0].type() == QNetworkProxy.NoProxy: # try it the Python way # scan the environment for variables named <scheme>_proxy # scan over whole environment to make this case insensitive for name, value in os.environ.items(): name = name.lower() if value and name[-6:] == '_proxy' and \ name[:-6] == query.protocolTag().lower(): url = QUrl(value) if url.scheme() == "http": proxyType = QNetworkProxy.HttpProxy elif url.scheme() == "https": proxyType = QNetworkProxy.HttpCachingProxy elif url.scheme() == "ftp": proxyType = QNetworkProxy.FtpCachingProxy else: proxyType = QNetworkProxy.HttpProxy proxy = QNetworkProxy(proxyType, url.host(), url.port(), url.userName(), url.password()) proxyList = [proxy] break if proxyList: scheme = schemeFromProxyType(proxyList[0].type()) if scheme == "": scheme = "Http" if scheme != "NoProxy": proxyList[0].setUser( Preferences.getUI("ProxyUser/{0}".format(scheme))) proxyList[0].setPassword( Preferences.getUI( "ProxyPassword/{0}".format(scheme))) return proxyList else: return [QNetworkProxy(QNetworkProxy.NoProxy)] else: if Preferences.getUI("UseHttpProxyForAll"): protocolKey = "Http" else: protocolKey = query.protocolTag().capitalize() host = Preferences.getUI("ProxyHost/{0}".format(protocolKey)) if not host: E5MessageBox.critical( None, QCoreApplication.translate( "E5NetworkProxyFactory", "Proxy Configuration Error"), QCoreApplication.translate( "E5NetworkProxyFactory", """Proxy usage was activated""" """ but no proxy host for protocol""" """ '{0}' configured.""").format(protocolKey)) return [QNetworkProxy(QNetworkProxy.DefaultProxy)] else: if protocolKey in ["Http", "Https", "Ftp"]: if query.protocolTag() == "ftp": proxyType = QNetworkProxy.FtpCachingProxy elif query.protocolTag() == "https": proxyType = QNetworkProxy.HttpCachingProxy else: proxyType = QNetworkProxy.HttpProxy proxy = QNetworkProxy( proxyType, host, Preferences.getUI("ProxyPort/" + protocolKey), Preferences.getUI("ProxyUser/" + protocolKey), Preferences.getUI("ProxyPassword/" + protocolKey)) else: proxy = QNetworkProxy(QNetworkProxy.DefaultProxy) return [proxy, QNetworkProxy(QNetworkProxy.DefaultProxy)] else: return [QNetworkProxy(QNetworkProxy.NoProxy)]
class CoverArtImage: # Indicate if types are provided by the source, ie. CAA or certain file # formats may have types associated with cover art, but some other sources # don't provide such information support_types = False # `is_front` has to be explicitly set, it is used to handle CAA is_front # indicator is_front = None sourceprefix = "URL" def __init__(self, url=None, types=None, comment='', data=None): if types is None: self.types = [] else: self.types = types if url is not None: self.parse_url(url) else: self.url = None self.comment = comment self.datahash = None # thumbnail is used to link to another CoverArtImage, ie. for PDFs self.thumbnail = None self.can_be_saved_to_tags = True self.can_be_saved_to_disk = True self.can_be_saved_to_metadata = True if data is not None: self.set_data(data) def parse_url(self, url): self.url = QUrl(url) self.host = string_(self.url.host()) self.port = self.url.port(80) self.path = string_(self.url.path(QUrl.FullyEncoded)) if self.url.hasQuery(): self.path += '?' + string_(self.url.query(QUrl.FullyEncoded)) @property def source(self): if self.url is not None: return "%s: %s" % (self.sourceprefix, self.url.toString()) else: return "%s" % self.sourceprefix def is_front_image(self): """Indicates if image is considered as a 'front' image. It depends on few things: - if `is_front` was set, it is used over anything else - if `types` was set, search for 'front' in it - if `support_types` is False, default to True for any image - if `support_types` is True, default to False for any image """ if not self.can_be_saved_to_metadata: # ignore thumbnails return False if self.is_front is not None: return self.is_front if 'front' in self.types: return True return (self.support_types is False) def imageinfo_as_string(self): if self.datahash is None: return "" return "w=%d h=%d mime=%s ext=%s datalen=%d file=%s" % (self.width, self.height, self.mimetype, self.extension, self.datalength, self.tempfile_filename) def __repr__(self): p = [] if self.url is not None: p.append("url=%r" % self.url.toString()) if self.types: p.append("types=%r" % self.types) if self.is_front is not None: p.append("is_front=%r" % self.is_front) if self.comment: p.append("comment=%r" % self.comment) return "%s(%s)" % (self.__class__.__name__, ", ".join(p)) def __str__(self): p = ['Image'] if self.url is not None: p.append("from %s" % self.url.toString()) if self.types: p.append("of type %s" % ','.join(self.types)) if self.comment: p.append("and comment '%s'" % self.comment) return ' '.join(p) def __eq__(self, other): if self and other: if self.types and other.types: return (self.datahash, self.types) == (other.datahash, other.types) else: return self.datahash == other.datahash elif not self and not other: return True else: return False def __hash__(self): if self.datahash is None: return 0 return hash(self.datahash.hash()) def set_data(self, data): """Store image data in a file, if data already exists in such file it will be re-used and no file write occurs """ if self.datahash: self.datahash.delete_file() self.datahash = None try: (self.width, self.height, self.mimetype, self.extension, self.datalength) = imageinfo.identify(data) except imageinfo.IdentificationError as e: raise CoverArtImageIdentificationError(e) try: self.datahash = DataHash(data, suffix=self.extension) except (OSError, IOError) as e: raise CoverArtImageIOError(e) @property def maintype(self): """Returns one type only, even for images having more than one type set. This is mostly used when saving cover art to tags because most formats don't support multiple types for one image. Images coming from CAA can have multiple types (ie. 'front, booklet'). """ if self.is_front_image() or not self.types or 'front' in self.types: return 'front' # TODO: do something better than randomly using the first in the list return self.types[0] def _make_image_filename(self, filename, dirname, metadata): filename = ScriptParser().eval(filename, metadata) if config.setting["ascii_filenames"]: if isinstance(filename, str): filename = unaccent(filename) filename = replace_non_ascii(filename) if not filename: filename = "cover" if not os.path.isabs(filename): filename = os.path.join(dirname, filename) # replace incompatible characters if config.setting["windows_compatibility"] or sys.platform == "win32": filename = replace_win32_incompat(filename) # remove null characters if isinstance(filename, bytes): filename = filename.replace(b"\x00", "") return encode_filename(filename) def save(self, dirname, metadata, counters): """Saves this image. :dirname: The name of the directory that contains the audio file :metadata: A metadata object :counters: A dictionary mapping filenames to the amount of how many images with that filename were already saved in `dirname`. """ if not self.can_be_saved_to_disk: return if (config.setting["caa_image_type_as_filename"] and not self.is_front_image()): filename = self.maintype log.debug("Make cover filename from types: %r -> %r", self.types, filename) else: filename = config.setting["cover_image_filename"] log.debug("Using default cover image filename %r", filename) filename = self._make_image_filename(filename, dirname, metadata) overwrite = config.setting["save_images_overwrite"] ext = encode_filename(self.extension) image_filename = self._next_filename(filename, counters) while os.path.exists(image_filename + ext) and not overwrite: if not self._is_write_needed(image_filename + ext): break image_filename = self._next_filename(filename, counters) else: new_filename = image_filename + ext # Even if overwrite is enabled we don't need to write the same # image multiple times if not self._is_write_needed(new_filename): return log.debug("Saving cover image to %r", new_filename) try: new_dirname = os.path.dirname(new_filename) if not os.path.isdir(new_dirname): os.makedirs(new_dirname) shutil.copyfile(self.tempfile_filename, new_filename) except (OSError, IOError) as e: raise CoverArtImageIOError(e) def _next_filename(self, filename, counters): if counters[filename]: new_filename = b"%b (%d)" % (filename, counters[filename]) else: new_filename = filename counters[filename] += 1 return new_filename def _is_write_needed(self, filename): if (os.path.exists(filename) and os.path.getsize(filename) == self.datalength): log.debug("Identical file size, not saving %r", filename) return False return True @property def data(self): """Reads the data from the temporary file created for this image. May raise CoverArtImageIOError """ try: return self.datahash.data except (OSError, IOError) as e: raise CoverArtImageIOError(e) @property def tempfile_filename(self): return self.datahash.filename def types_as_string(self, translate=True, separator=', '): if self.types: types = self.types elif self.is_front_image(): types = ['front'] else: types = ['-'] if translate: types = [translate_caa_type(type) for type in types] return separator.join(types)
class CoverArtImage: # Indicate if types are provided by the source, ie. CAA or certain file # formats may have types associated with cover art, but some other sources # don't provide such information support_types = False # Indicates that the source supports multiple types per image. support_multi_types = False # `is_front` has to be explicitly set, it is used to handle CAA is_front # indicator is_front = None sourceprefix = "URL" def __init__(self, url=None, types=None, comment='', data=None, support_types=None, support_multi_types=None): if types is None: self.types = [] else: self.types = types if url is not None: self.parse_url(url) else: self.url = None self.comment = comment self.datahash = None # thumbnail is used to link to another CoverArtImage, ie. for PDFs self.thumbnail = None self.can_be_saved_to_tags = True self.can_be_saved_to_disk = True self.can_be_saved_to_metadata = True if support_types is not None: self.support_types = support_types if support_multi_types is not None: self.support_multi_types = support_multi_types if data is not None: self.set_data(data) def parse_url(self, url): self.url = QUrl(url) self.host = self.url.host() self.port = self.url.port(443 if self.url.scheme() == 'https' else 80) self.path = self.url.path(QUrl.FullyEncoded) if self.url.hasQuery(): self.path += '?' + self.url.query(QUrl.FullyEncoded) @property def source(self): if self.url is not None: return "%s: %s" % (self.sourceprefix, self.url.toString()) else: return "%s" % self.sourceprefix def is_front_image(self): """Indicates if image is considered as a 'front' image. It depends on few things: - if `is_front` was set, it is used over anything else - if `types` was set, search for 'front' in it - if `support_types` is False, default to True for any image - if `support_types` is True, default to False for any image """ if not self.can_be_saved_to_metadata: # ignore thumbnails return False if self.is_front is not None: return self.is_front if 'front' in self.types: return True return (self.support_types is False) def imageinfo_as_string(self): if self.datahash is None: return "" return "w=%d h=%d mime=%s ext=%s datalen=%d file=%s" % ( self.width, self.height, self.mimetype, self.extension, self.datalength, self.tempfile_filename) def __repr__(self): p = [] if self.url is not None: p.append("url=%r" % self.url.toString()) if self.types: p.append("types=%r" % self.types) p.append('support_types=%r' % self.support_types) p.append('support_multi_types=%r' % self.support_types) if self.is_front is not None: p.append("is_front=%r" % self.is_front) if self.comment: p.append("comment=%r" % self.comment) return "%s(%s)" % (self.__class__.__name__, ", ".join(p)) def __str__(self): p = ['Image'] if self.url is not None: p.append("from %s" % self.url.toString()) if self.types: p.append("of type %s" % ','.join(self.types)) if self.comment: p.append("and comment '%s'" % self.comment) return ' '.join(p) def __eq__(self, other): if self and other: if self.support_types and other.support_types: if self.support_multi_types and other.support_multi_types: return (self.datahash, self.types) == (other.datahash, other.types) else: return (self.datahash, self.maintype) == (other.datahash, other.maintype) else: return self.datahash == other.datahash elif not self and not other: return True else: return False def __hash__(self): if self.datahash is None: return 0 return hash(self.datahash.hash()) def set_data(self, data): """Store image data in a file, if data already exists in such file it will be re-used and no file write occurs """ if self.datahash: self.datahash.delete_file() self.datahash = None try: (self.width, self.height, self.mimetype, self.extension, self.datalength) = imageinfo.identify(data) except imageinfo.IdentificationError as e: raise CoverArtImageIdentificationError(e) try: self.datahash = DataHash(data, suffix=self.extension) except (OSError, IOError) as e: raise CoverArtImageIOError(e) @property def maintype(self): """Returns one type only, even for images having more than one type set. This is mostly used when saving cover art to tags because most formats don't support multiple types for one image. Images coming from CAA can have multiple types (ie. 'front, booklet'). """ if self.is_front_image() or not self.types or 'front' in self.types: return 'front' # TODO: do something better than randomly using the first in the list return self.types[0] def _make_image_filename(self, filename, dirname, _metadata): metadata = Metadata() metadata.copy(_metadata) metadata["coverart_maintype"] = self.maintype metadata["coverart_comment"] = self.comment if self.is_front: metadata.add_unique("coverart_types", "front") for cover_type in self.types: metadata.add_unique("coverart_types", cover_type) filename = script_to_filename(filename, metadata) if not filename: filename = "cover" if not os.path.isabs(filename): filename = os.path.join(dirname, filename) return encode_filename(filename) def save(self, dirname, metadata, counters): """Saves this image. :dirname: The name of the directory that contains the audio file :metadata: A metadata object :counters: A dictionary mapping filenames to the amount of how many images with that filename were already saved in `dirname`. """ if not self.can_be_saved_to_disk: return if (config.setting["caa_image_type_as_filename"] and not self.is_front_image()): filename = self.maintype log.debug("Make cover filename from types: %r -> %r", self.types, filename) else: filename = config.setting["cover_image_filename"] log.debug("Using default cover image filename %r", filename) filename = self._make_image_filename(filename, dirname, metadata) overwrite = config.setting["save_images_overwrite"] ext = encode_filename(self.extension) image_filename = self._next_filename(filename, counters) while os.path.exists(image_filename + ext) and not overwrite: if not self._is_write_needed(image_filename + ext): break image_filename = self._next_filename(filename, counters) else: new_filename = image_filename + ext # Even if overwrite is enabled we don't need to write the same # image multiple times if not self._is_write_needed(new_filename): return log.debug("Saving cover image to %r", new_filename) try: new_dirname = os.path.dirname(new_filename) if not os.path.isdir(new_dirname): os.makedirs(new_dirname) shutil.copyfile(self.tempfile_filename, new_filename) except (OSError, IOError) as e: raise CoverArtImageIOError(e) def _next_filename(self, filename, counters): if counters[filename]: new_filename = "%s (%d)" % (decode_filename(filename), counters[filename]) else: new_filename = filename counters[filename] += 1 return encode_filename(new_filename) def _is_write_needed(self, filename): if (os.path.exists(filename) and os.path.getsize(filename) == self.datalength): log.debug("Identical file size, not saving %r", filename) return False return True @property def data(self): """Reads the data from the temporary file created for this image. May raise CoverArtImageIOError """ try: return self.datahash.data except (OSError, IOError) as e: raise CoverArtImageIOError(e) @property def tempfile_filename(self): return self.datahash.filename def normalized_types(self): if self.types: types = sorted(set(self.types)) elif self.is_front_image(): types = ['front'] else: types = ['-'] return types def types_as_string(self, translate=True, separator=', '): types = self.normalized_types() if translate: types = [translate_caa_type(type) for type in types] return separator.join(types)
def queryProxy(self, query): """ Public method to determine a proxy for a given query. @param query reference to the query object (QNetworkProxyQuery) @return list of proxies in order of preference (list of QNetworkProxy) """ if query.queryType() == QNetworkProxyQuery.UrlRequest and query.protocolTag() in ["http", "https", "ftp"]: # use proxy at all ? if not Preferences.getUI("UseProxy"): return [QNetworkProxy(QNetworkProxy.NoProxy)] # test for exceptions exceptions = Preferences.getUI("ProxyExceptions") if exceptions != self.__exceptions: self.__setExceptions(exceptions) urlHost = query.url().host() for matcher in self.__hostnameMatchers: if matcher.match(urlHost): return [QNetworkProxy(QNetworkProxy.NoProxy)] # determine proxy if Preferences.getUI("UseSystemProxy"): proxyList = QNetworkProxyFactory.systemProxyForQuery(query) if ( not Globals.isWindowsPlatform() and len(proxyList) == 1 and proxyList[0].type() == QNetworkProxy.NoProxy ): # try it the Python way # scan the environment for variables named <scheme>_proxy # scan over whole environment to make this case insensitive for name, value in os.environ.items(): name = name.lower() if value and name[-6:] == "_proxy" and name[:-6] == query.protocolTag().lower(): url = QUrl(value) if url.scheme() == "http": proxyType = QNetworkProxy.HttpProxy elif url.scheme() == "https": proxyType = QNetworkProxy.HttpCachingProxy elif url.scheme() == "ftp": proxyType = QNetworkProxy.FtpCachingProxy else: proxyType = QNetworkProxy.HttpProxy proxy = QNetworkProxy(proxyType, url.host(), url.port(), url.userName(), url.password()) proxyList = [proxy] break if proxyList: scheme = schemeFromProxyType(proxyList[0].type()) if scheme == "": scheme = "Http" if scheme != "NoProxy": proxyList[0].setUser(Preferences.getUI("ProxyUser/{0}".format(scheme))) proxyList[0].setPassword(Preferences.getUI("ProxyPassword/{0}".format(scheme))) return proxyList else: return [QNetworkProxy(QNetworkProxy.NoProxy)] else: if Preferences.getUI("UseHttpProxyForAll"): protocolKey = "Http" else: protocolKey = query.protocolTag().capitalize() host = Preferences.getUI("ProxyHost/{0}".format(protocolKey)) if not host: E5MessageBox.critical( None, QCoreApplication.translate("E5NetworkProxyFactory", "Proxy Configuration Error"), QCoreApplication.translate( "E5NetworkProxyFactory", """Proxy usage was activated""" """ but no proxy host for protocol""" """ '{0}' configured.""", ).format(protocolKey), ) return [QNetworkProxy(QNetworkProxy.DefaultProxy)] else: if protocolKey in ["Http", "Https", "Ftp"]: if query.protocolTag() == "ftp": proxyType = QNetworkProxy.FtpCachingProxy elif query.protocolTag() == "https": proxyType = QNetworkProxy.HttpCachingProxy else: proxyType = QNetworkProxy.HttpProxy proxy = QNetworkProxy( proxyType, host, Preferences.getUI("ProxyPort/" + protocolKey), Preferences.getUI("ProxyUser/" + protocolKey), Preferences.getUI("ProxyPassword/" + protocolKey), ) else: proxy = QNetworkProxy(QNetworkProxy.DefaultProxy) return [proxy, QNetworkProxy(QNetworkProxy.DefaultProxy)] else: return [QNetworkProxy(QNetworkProxy.NoProxy)]