def __guessUrlFromPath(self, path): """ Private method to guess an URL given a path string. @param path path string to guess an URL for (string) @return guessed URL (QUrl) """ manager = self.__mainWindow.openSearchManager() path = Utilities.fromNativeSeparators(path) url = manager.convertKeywordSearchToUrl(path) if url.isValid(): return url try: url = QUrl.fromUserInput(path) except AttributeError: url = QUrl(path) if url.scheme() == "about" and \ url.path() == "home": url = QUrl("eric:home") if url.scheme() in ["s", "search"]: url = manager.currentEngine().searchUrl(url.path().strip()) if url.scheme() != "" and \ (url.host() != "" or url.path() != ""): return url urlString = Preferences.getHelp("DefaultScheme") + path.strip() url = QUrl.fromEncoded(urlString.encode("utf-8"), QUrl.TolerantMode) return url
def _has_explicit_scheme(url: QUrl): """Check if a url has an explicit scheme given. Args: url: The URL as QUrl. """ # Note that generic URI syntax actually would allow a second colon # after the scheme delimiter. Since we don't know of any URIs # using this and want to support e.g. searching for scoped C++ # symbols, we treat this as not a URI anyways. return (url.isValid() and url.scheme() and (url.host() or url.path()) and " " not in url.path() and not url.path().startswith(":"))
def _linkClicked(self, url: QUrl) -> None: scheme = url.scheme() if scheme == 'file': # Open the file in an external application. utilities.openFile(url.path()) elif scheme == 'http' or scheme == 'https': # Open the web page in the default browser. webbrowser.open_new_tab(url.toString()) elif scheme == 'item': # Clear the search and select the item (if present in the tree). self._searchEdit.clear() self._model.searchText = '' index = self._model.findItemById(url.path()) if index.isValid(): self._treeView.setCurrentIndex(index)
def qute_pdfjs(url: QUrl) -> _HandlerRet: """Handler for qute://pdfjs. Return the pdf.js viewer or redirect to original URL if the file does not exist. """ if url.path() == '/file': filename = QUrlQuery(url).queryItemValue('filename') if not filename: raise UrlInvalidError("Missing filename") if '/' in filename or os.sep in filename: raise RequestDeniedError("Path separator in filename.") path = _pdf_path(filename) with open(path, 'rb') as f: data = f.read() mimetype = utils.guess_mimetype(filename, fallback=True) return mimetype, data if url.path() == '/web/viewer.html': query = QUrlQuery(url) filename = query.queryItemValue("filename") if not filename: raise UrlInvalidError("Missing filename") path = _pdf_path(filename) if not os.path.isfile(path): source = query.queryItemValue('source') if not source: # This may happen with old URLs stored in history raise UrlInvalidError("Missing source") raise Redirect(QUrl(source)) data = pdfjs.generate_pdfjs_page(filename, url) return 'text/html', data try: data = pdfjs.get_pdfjs_res(url.path()) except pdfjs.PDFJSNotFound as e: # Logging as the error might get lost otherwise since we're not showing # the error page if a single asset is missing. This way we don't lose # information, as the failed pdfjs requests are still in the log. log.misc.warning( "pdfjs resource requested but not found: {}".format(e.path)) raise NotFoundError("Can't find pdfjs resource '{}'".format(e.path)) else: mimetype = utils.guess_mimetype(url.fileName(), fallback=True) return mimetype, data
def qute_settings(url: QUrl) -> _HandlerRet: """Handler for qute://settings. View/change qute configuration.""" global csrf_token if url.path() == '/set': if url.password() != csrf_token: message.error("Invalid CSRF token for qute://settings!") raise RequestDeniedError("Invalid CSRF token!") return _qute_settings_set(url) # Requests to qute://settings/set should only be allowed from # qute://settings. As an additional security precaution, we generate a CSRF # token to use here. if secrets: csrf_token = secrets.token_urlsafe() else: # On Python < 3.6, from secrets.py token = base64.urlsafe_b64encode(os.urandom(32)) csrf_token = token.rstrip(b'=').decode('ascii') src = jinja.render('settings.html', title='settings', configdata=configdata, confget=config.instance.get_str, csrf_token=csrf_token) return 'text/html', src
def __init__(self, qml): app = QGuiApplication(sys.argv) model = QmlModel() model.register() qmlUrl=QUrl(qml) assert qmlUrl.isValid() print(qmlUrl.path()) #assert qmlUrl.isLocalFile() """ Create an engine a reference to the window? window = QuickWindowFactory().create(qmlUrl=qmlUrl) window.show() # visible """ engine = QQmlApplicationEngine() ''' Need this if no stdio, i.e. Android, OSX, iOS. OW, qWarnings to stdio. engine.warnings.connect(self.errors) ''' engine.load(qmlUrl) engine.quit.connect(app.quit) app.exec_() # !!! C exec => Python exec_ print("Application returned")
def acceptNavigationRequest(self, url: QUrl, _type, isMainFrame): """При запросе урла со схемой file возбуждает событие и запрещает загрузку этого урла""" if url.scheme() == 'file': QCoreApplication.sendEvent(self.parent(), MyEvent(url.path())) return False return super(WebEnginePage, self).acceptNavigationRequest(url, _type, isMainFrame)
def load_group(self, file: QUrl): group_loader = GroupLoader.auto(file=file.path(), professor=self.professor, session=self.loading_session) group = group_loader.get_group() self._apply_group(group.name, group_loader.get_students_list())
def filename_from_url(url: QUrl, fallback: str = None) -> Optional[str]: """Get a suitable filename from a URL. Args: url: The URL to parse, as a QUrl. fallback: Value to use if no name can be determined. Return: The suggested filename as a string, or None. """ if not url.isValid(): return fallback if url.scheme().lower() == 'data': mimetype, _encoding = mimetypes.guess_type(url.toString()) if not mimetype: return fallback ext = utils.mimetype_extension(mimetype) or '' return 'download' + ext pathname = posixpath.basename(url.path()) if pathname: return pathname elif url.host(): return url.host() + '.html' else: return fallback
def test_client(): q=QUrl("entry: abc") print(q.toString().split(":")[1].strip("/ ")) print(q.scheme()) print(q.host()) print(q.path()) print(q.fragment())
def test_failed_dl_update(config_stub, basedir, download_stub, data_tmpdir, tmpdir, win_registry): """One blocklist fails to download. Ensure hosts from this list are not blocked. """ dl_fail_blocklist = QUrl(create_blocklist(tmpdir, blocked_hosts=CLEAN_HOSTS, name='download_will_fail', line_format='one_per_line')) hosts_to_block = generic_blocklists(tmpdir) + [dl_fail_blocklist] config_stub.data = { 'content': { 'host-block-lists': hosts_to_block, 'host-blocking-enabled': True, 'host-blocking-whitelist': None, } } host_blocker = adblock.HostBlocker() host_blocker.adblock_update(0) while host_blocker._in_progress: current_download = host_blocker._in_progress[0] # if current download is the file we want to fail, make it fail if current_download.name == dl_fail_blocklist.path(): current_download.successful = False current_download.finished.emit() host_blocker.read_hosts() assert_urls(host_blocker, whitelisted=[])
def __init__(self, qml): app = QGuiApplication(sys.argv) model = QmlModel() model.register() qmlUrl = QUrl(qml) assert qmlUrl.isValid() print(qmlUrl.path()) # assert qmlUrl.isLocalFile() """ Create an engine a reference to the window? window = QuickWindowFactory().create(qmlUrl=qmlUrl) window.show() # visible """ engine = QQmlApplicationEngine() """ Need this if no stdio, i.e. Android, OSX, iOS. OW, qWarnings to stdio. engine.warnings.connect(self.errors) """ engine.load(qmlUrl) engine.quit.connect(app.quit) app.exec_() # !!! C exec => Python exec_ print("Application returned")
def _the_compiler_redir(url: QUrl) -> bool: p = url.path().strip('/') res = re.search(r"\w+$", p) if p.startswith('view/') and res: url.setPath(urljoin("/view/raw/", res.group())) return True return False
def test_failed_dl_update(config_stub, basedir, download_stub, data_tmpdir, tmpdir, win_registry): """One blocklist fails to download. Ensure hosts from this list are not blocked. """ dl_fail_blocklist = QUrl( create_blocklist(tmpdir, blocked_hosts=CLEAN_HOSTS, name='download_will_fail', line_format='one_per_line')) hosts_to_block = generic_blocklists(tmpdir) + [dl_fail_blocklist] config_stub.data = { 'content': { 'host-block-lists': hosts_to_block, 'host-blocking-enabled': True, 'host-blocking-whitelist': None, } } host_blocker = adblock.HostBlocker() host_blocker.adblock_update(0) while host_blocker._in_progress: current_download = host_blocker._in_progress[0] # if current download is the file we want to fail, make it fail if current_download.name == dl_fail_blocklist.path(): current_download.successful = False current_download.finished.emit() host_blocker.read_hosts() assert_urls(host_blocker, whitelisted=[])
def acceptNavigationRequest(self, url: QtCore.QUrl, type: 'QWebEnginePage.NavigationType', isMainFrame: bool): """ Overrides the default method Returns True if navigation is OK, returns False otherwise """ text = url.path() if text.startswith('/action'): id = int(text[7:]) if self.callback is None: raise Exception( 'callback not set - please set this.callback = your_function' ) try: data = self.action_data[id] except: raise Exception( 'data with id = {} is not available. Did you accidentally clear actions since creating the link?' .format(id)) self.callback(data) return False return True
def open_in_browser(self, url: QtCore.QUrl) -> None: """Open links in browser :param url: Url of file """ if url.path()[-3:] != ".md": self.back() QtGui.QDesktopServices.openUrl(url)
def checkdownloadFileExists(self, downloadPath): if (not downloadPath or not downloadPath): return False filePath = QUrl(downloadPath) fileInfo = QFileInfo(filePath.path()) fileName = fileInfo.fileName() if QFile.exists(fileName): QFile.remove(fileName) return True
def onbuttonInsertPiture(self): url = self.lineEditInserPiture.text() if url != "": url = QUrl(url) self.textEditActualresults.moveCursor(QTextCursor.End) self.textEditActualresults.insertPlainText( """\n<img src="{}"/>""".format(url.path())) self.textEditActualresults.moveCursor(QTextCursor.End) self.lineEditInserPiture.clear()
def qute_resource(url: QUrl) -> _HandlerRet: """Handler for qute://resource.""" path = url.path().lstrip('/') mimetype = utils.guess_mimetype(path, fallback=True) try: data = resources.read_file_binary(path) except FileNotFoundError as e: raise NotFoundError(str(e)) return mimetype, data
def data_for_url(url: QUrl) -> Tuple[str, bytes]: """Get the data to show for the given URL. Args: url: The QUrl to show. Return: A (mimetype, data) tuple. """ norm_url = url.adjusted( QUrl.NormalizePathSegments | # type: ignore[arg-type] QUrl.StripTrailingSlash) if norm_url != url: raise Redirect(norm_url) path = url.path() host = url.host() query = url.query() # A url like "qute:foo" is split as "scheme:path", not "scheme:host". log.misc.debug("url: {}, path: {}, host {}".format( url.toDisplayString(), path, host)) if not path or not host: new_url = QUrl() new_url.setScheme('qute') # When path is absent, e.g. qute://help (with no trailing slash) if host: new_url.setHost(host) # When host is absent, e.g. qute:help else: new_url.setHost(path) new_url.setPath('/') if query: new_url.setQuery(query) if new_url.host(): # path was a valid host raise Redirect(new_url) try: handler = _HANDLERS[host] except KeyError: raise NotFoundError("No handler found for {}".format( url.toDisplayString())) try: mimetype, data = handler(url) except OSError as e: raise SchemeOSError(e) assert mimetype is not None, url if mimetype == 'text/html' and isinstance(data, str): # We let handlers return HTML as text data = data.encode('utf-8', errors='xmlcharrefreplace') assert isinstance(data, bytes) return mimetype, data
def paths_to_items_list(parent, paths): items = [] for path in paths: url = QUrl(path) if type(path) == QUrl else path path = url.path() item = QTreeWidgetItem(parent, [ path ]) item.setData(0, SubtleTreeWidget.ITEM_ROLE, url) item.setText(0, os.path.basename(path)) item.setToolTip(0, path) items.append( item ) return items
def paths_to_items_list(parent, paths): items = [] for path in paths: url = QUrl(path) if type(path) == QUrl else path path = url.path() item = QTreeWidgetItem(parent, [path]) item.setData(0, SubtleTreeWidget.ITEM_ROLE, url) item.setText(0, os.path.basename(path)) item.setToolTip(0, path) items.append(item) return items
def qute_javascript(url: QUrl) -> _HandlerRet: """Handler for qute://javascript. Return content of file given as query parameter. """ path = url.path() if path: path = "javascript" + os.sep.join(path.split('/')) return 'text/html', utils.read_file(path, binary=False) else: raise UrlInvalidError("No file specified")
def qute_configdiff(url: QUrl) -> _HandlerRet: """Handler for qute://configdiff.""" if url.path() == '/old': try: return 'text/html', configdiff.get_diff() except OSError as e: error = (b'Failed to read old config: ' + str(e.strerror).encode('utf-8')) return 'text/plain', error else: data = config.instance.dump_userconfig().encode('utf-8') return 'text/plain', data
def _map_to_signal(self, url: QUrl): """ This slot delegates clicked link signal to show the same html as before (eg. link to a point in the term has been clicked) or to delegates the filename given in the link as emitted signal_link_clicked(filename) signal. """ if url.fileName() == "" and url.path() == "/": logging.debug(str(url)) self.ui.contentWebView.setHtml(self._html, url) else: self.signal_link_clicked.emit(url.toLocalFile().split("/")[-1])
def data_for_url(url: QUrl): """Get the data to show for the given URL. Args: url: The QUrl to show. Return: A (mimetype, data) tuple. """ norm_url = url.adjusted(QUrl.NormalizePathSegments | QUrl.StripTrailingSlash) if norm_url != url: raise Redirect(norm_url) path = url.path() host = url.host() query = urlutils.query_string(url) # A url like "luminos:foo" is split as "scheme:path", not "scheme:host". if not path or not host: new_url = QUrl() new_url.setScheme("luminos") # When path is absent, e.g. luminos://help (with no trailing slash) if host: new_url.setHost(host) # When host is absent, e.g. luminos:help else: new_url.setHost(path) new_url.setPath("/") if query: new_url.setQuery(query) if new_url.host(): # path was a valid host raise Redirect(new_url) try: handler = _HANDLERS[host] except KeyError: raise NotFoundError("No handler found for {}".format( url.toDisplayString())) try: mimetype, data = handler(url) except OSError as e: raise SchemeOSError(e) assert mimetype is not None, url if mimetype == "text/html" and isinstance(data, str): # We let handlers return HTML as text data = data.encode("utf-8", errors="xmlcharrefreplace") return mimetype, data
def qute_warning(url: QUrl) -> _HandlerRet: """Handler for qute://warning.""" path = url.path() if path == '/webkit': src = jinja.render('warning-webkit.html', title='QtWebKit backend warning') elif path == '/sessions': src = jinja.render('warning-sessions.html', title='Qt 5.15 sessions warning', datadir=standarddir.data(), sep=os.sep) else: raise NotFoundError("Invalid warning page {}".format(path)) return 'text/html', src
def __init__(self, connections=None, 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") if connections is None: connections = [] 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 loadResource(self, resource_type: int, name: QtCore.QUrl) -> Union[QtGui.QImage, None]: logger.debug("loadResource: %r, %r", resource_type, name) if resource_type == QtGui.QTextDocument.ImageResource: name = name.path() if name.startswith("/file/download/"): name = name[len("/file/download/"):] if name in self.resourceMapCache: if self.resourceMapCache[name] is not None: return QtGui.QImage( self.resourceMapCache[name]["filename"]) else: RemoteFile(name, self.onFileAvaiable) self.resourceMapCache[name] = None return None
def test_resource_url(): """Test resource_url() which can be used from templates.""" data = jinja.render('test2.html') print(data) url = QUrl(data) assert url.isValid() assert url.scheme() == 'file' path = url.path() if os.name == "nt": path = path.lstrip('/') path = path.replace('/', os.sep) with open(path, 'r', encoding='utf-8') as f: assert f.read().splitlines()[0] == "Hello World!"
def test_resource_url(): """Test resource_url() which can be used from templates.""" data = jinja.render('resource_url.html') print(data) url = QUrl(data) assert url.isValid() assert url.scheme() == 'file' path = url.path() if os.name == "nt": path = path.lstrip('/') path = path.replace('/', os.sep) with open(path, 'r', encoding='utf-8') as f: assert f.read().splitlines()[0] == "Hello World!"
def qute_help(url: QUrl) -> _HandlerRet: """Handler for qute://help.""" urlpath = url.path() if not urlpath or urlpath == '/': urlpath = 'index.html' else: urlpath = urlpath.lstrip('/') if not docutils.docs_up_to_date(urlpath): message.error("Your documentation is outdated! Please re-run " "scripts/asciidoc2html.py.") path = 'html/doc/{}'.format(urlpath) if not urlpath.endswith('.html'): try: bdata = utils.read_file(path, binary=True) except OSError as e: raise SchemeOSError(e) mimetype = utils.guess_mimetype(urlpath) return mimetype, bdata try: data = utils.read_file(path) except OSError: asciidoc = _asciidoc_fallback_path(path) if asciidoc is None: raise preamble = textwrap.dedent(""" There was an error loading the documentation! This most likely means the documentation was not generated properly. If you are running qutebrowser from the git repository, please (re)run scripts/asciidoc2html.py and reload this page. If you're running a released version this is a bug, please use :report to report it. Falling back to the plaintext version. --------------------------------------------------------------- """) return 'text/plain', (preamble + asciidoc).encode('utf-8') else: return 'text/html', data
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 test_resource_url(): """Test resource_url() which can be used from templates.""" template = jinja.env.get_template("test2.html") data = template.render() # pylint: disable=no-member print(data) url = QUrl(data) assert url.isValid() assert url.scheme() == "file" path = url.path() if os.name == "nt": path = path.lstrip("/") path = path.replace("/", os.sep) with open(path, "r", encoding="utf-8") as f: assert f.read().splitlines()[0] == "Hello World!"
def __stripUrl(self, url): """ Private method to strip off all unneeded parts of a URL. @param url URL to be stripped (QUrl) @return stripped URL (QUrl) """ cleanUrl = QUrl(url) cleanUrl.setQuery("") cleanUrl.setUserInfo("") authority = cleanUrl.authority() if authority.startswith("@"): authority = authority[1:] cleanUrl = QUrl("{0}://{1}{2}".format(cleanUrl.scheme(), authority, cleanUrl.path())) cleanUrl.setFragment("") return cleanUrl
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 filename_from_url(url: QUrl) -> Optional[str]: """Get a suitable filename from a URL. Args: url: The URL to parse, as a QUrl. Return: The suggested filename as a string, or None. """ if not url.isValid(): return None pathname = posixpath.basename(url.path()) if pathname: return pathname elif url.host(): return url.host() + '.html' else: return None
def __stripUrl(self, url): """ Private method to strip off all unneeded parts of a URL. @param url URL to be stripped (QUrl) @return stripped URL (QUrl) """ cleanUrl = QUrl(url) if qVersion() >= "5.0.0": cleanUrl.setQuery("") else: cleanUrl.setQueryItems([]) cleanUrl.setUserInfo("") authority = cleanUrl.authority() if authority.startswith("@"): authority = authority[1:] cleanUrl = QUrl("{0}://{1}{2}".format( cleanUrl.scheme(), authority, cleanUrl.path())) cleanUrl.setFragment("") return cleanUrl
def test_failed_dl_update(config_stub, basedir, download_stub, data_tmpdir, tmpdir, win_registry, caplog): """One blocklist fails to download. Ensure hosts from this list are not blocked. """ dl_fail_blocklist = QUrl( create_blocklist(tmpdir, blocked_hosts=CLEAN_HOSTS, name="download_will_fail", line_format="one_per_line") ) hosts_to_block = generic_blocklists(tmpdir) + [dl_fail_blocklist] config_stub.data = { "content": {"host-block-lists": hosts_to_block, "host-blocking-enabled": True, "host-blocking-whitelist": None} } host_blocker = adblock.HostBlocker() host_blocker.adblock_update() while host_blocker._in_progress: current_download = host_blocker._in_progress[0] # if current download is the file we want to fail, make it fail if current_download.name == dl_fail_blocklist.path(): current_download.successful = False with caplog.at_level(logging.ERROR): current_download.finished.emit() host_blocker.read_hosts() assert_urls(host_blocker, whitelisted=[])
class HttpWindow(QDialog): def __init__(self, parent=None): super(HttpWindow, self).__init__(parent) self.url = QUrl() self.qnam = QNetworkAccessManager() self.reply = None self.outFile = None self.httpGetId = 0 self.httpRequestAborted = False self.urlLineEdit = QLineEdit('https://www.qt.io') urlLabel = QLabel("&URL:") urlLabel.setBuddy(self.urlLineEdit) self.statusLabel = QLabel( "Please enter the URL of a file you want to download.") self.statusLabel.setWordWrap(True) self.downloadButton = QPushButton("Download") self.downloadButton.setDefault(True) self.quitButton = QPushButton("Quit") self.quitButton.setAutoDefault(False) buttonBox = QDialogButtonBox() buttonBox.addButton(self.downloadButton, QDialogButtonBox.ActionRole) buttonBox.addButton(self.quitButton, QDialogButtonBox.RejectRole) self.progressDialog = QProgressDialog(self) self.urlLineEdit.textChanged.connect(self.enableDownloadButton) self.qnam.authenticationRequired.connect( self.slotAuthenticationRequired) self.qnam.sslErrors.connect(self.sslErrors) self.progressDialog.canceled.connect(self.cancelDownload) self.downloadButton.clicked.connect(self.downloadFile) self.quitButton.clicked.connect(self.close) topLayout = QHBoxLayout() topLayout.addWidget(urlLabel) topLayout.addWidget(self.urlLineEdit) mainLayout = QVBoxLayout() mainLayout.addLayout(topLayout) mainLayout.addWidget(self.statusLabel) mainLayout.addWidget(buttonBox) self.setLayout(mainLayout) self.setWindowTitle("HTTP") self.urlLineEdit.setFocus() def startRequest(self, url): self.reply = self.qnam.get(QNetworkRequest(url)) self.reply.finished.connect(self.httpFinished) self.reply.readyRead.connect(self.httpReadyRead) self.reply.downloadProgress.connect(self.updateDataReadProgress) def downloadFile(self): self.url = QUrl(self.urlLineEdit.text()) fileInfo = QFileInfo(self.url.path()) fileName = fileInfo.fileName() if not fileName: fileName = 'index.html' if QFile.exists(fileName): ret = QMessageBox.question(self, "HTTP", "There already exists a file called %s in the current " "directory. Overwrite?" % fileName, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if ret == QMessageBox.No: return QFile.remove(fileName) self.outFile = QFile(fileName) if not self.outFile.open(QIODevice.WriteOnly): QMessageBox.information(self, "HTTP", "Unable to save the file %s: %s." % (fileName, self.outFile.errorString())) self.outFile = None return self.progressDialog.setWindowTitle("HTTP") self.progressDialog.setLabelText("Downloading %s." % fileName) self.downloadButton.setEnabled(False) self.httpRequestAborted = False self.startRequest(self.url) def cancelDownload(self): self.statusLabel.setText("Download canceled.") self.httpRequestAborted = True if self.reply is not None: self.reply.abort() self.downloadButton.setEnabled(True) def httpFinished(self): if self.httpRequestAborted: if self.outFile is not None: self.outFile.close() self.outFile.remove() self.outFile = None self.reply.deleteLater() self.reply = None self.progressDialog.hide() return self.progressDialog.hide() self.outFile.flush() self.outFile.close() redirectionTarget = self.reply.attribute(QNetworkRequest.RedirectionTargetAttribute) if self.reply.error(): self.outFile.remove() QMessageBox.information(self, "HTTP", "Download failed: %s." % self.reply.errorString()) self.downloadButton.setEnabled(True) elif redirectionTarget is not None: newUrl = self.url.resolved(redirectionTarget) ret = QMessageBox.question(self, "HTTP", "Redirect to %s?" % newUrl.toString(), QMessageBox.Yes | QMessageBox.No) if ret == QMessageBox.Yes: self.url = newUrl self.reply.deleteLater() self.reply = None self.outFile.open(QIODevice.WriteOnly) self.outFile.resize(0) self.startRequest(self.url) return else: fileName = QFileInfo(QUrl(self.urlLineEdit.text()).path()).fileName() self.statusLabel.setText("Downloaded %s to %s." % (fileName, QDir.currentPath())) self.downloadButton.setEnabled(True) self.reply.deleteLater() self.reply = None self.outFile = None def httpReadyRead(self): if self.outFile is not None: self.outFile.write(self.reply.readAll()) def updateDataReadProgress(self, bytesRead, totalBytes): if self.httpRequestAborted: return self.progressDialog.setMaximum(totalBytes) self.progressDialog.setValue(bytesRead) def enableDownloadButton(self): self.downloadButton.setEnabled(self.urlLineEdit.text() != '') def slotAuthenticationRequired(self, authenticator): import os from PyQt5 import uic ui = os.path.join(os.path.dirname(__file__), 'authenticationdialog.ui') dlg = uic.loadUi(ui) dlg.adjustSize() dlg.siteDescription.setText("%s at %s" % (authenticator.realm(), self.url.host())) dlg.userEdit.setText(self.url.userName()) dlg.passwordEdit.setText(self.url.password()) if dlg.exec_() == QDialog.Accepted: authenticator.setUser(dlg.userEdit.text()) authenticator.setPassword(dlg.passwordEdit.text()) def sslErrors(self, reply, errors): errorString = ", ".join([str(error.errorString()) for error in errors]) ret = QMessageBox.warning(self, "HTTP Example", "One or more SSL errors has occurred: %s" % errorString, QMessageBox.Ignore | QMessageBox.Abort) if ret == QMessageBox.Ignore: self.reply.ignoreSslErrors()
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)