First debugging implementation of downloads

This commit is contained in:
Florian Bruhin 2014-06-10 22:11:17 +02:00
parent 71bc640131
commit 7dd5b1b94e
5 changed files with 118 additions and 1 deletions

View File

@ -53,6 +53,7 @@ from qutebrowser.commands.managers import CommandManager, SearchManager
from qutebrowser.config.iniparsers import ReadWriteConfigParser from qutebrowser.config.iniparsers import ReadWriteConfigParser
from qutebrowser.config.lineparser import LineConfigParser from qutebrowser.config.lineparser import LineConfigParser
from qutebrowser.browser.cookies import CookieJar from qutebrowser.browser.cookies import CookieJar
from qutebrowser.browser.downloads import DownloadManager
from qutebrowser.utils.message import MessageBridge from qutebrowser.utils.message import MessageBridge
from qutebrowser.utils.misc import (get_standard_dir, actute_warning, from qutebrowser.utils.misc import (get_standard_dir, actute_warning,
get_qt_args) get_qt_args)
@ -132,6 +133,7 @@ class Application(QApplication):
self.networkmanager = NetworkManager(self.cookiejar) self.networkmanager = NetworkManager(self.cookiejar)
self.commandmanager = CommandManager() self.commandmanager = CommandManager()
self.searchmanager = SearchManager() self.searchmanager = SearchManager()
self.downloadmanager = DownloadManager()
self.mainwindow = MainWindow() self.mainwindow = MainWindow()
self.modeman.mainwindow = self.mainwindow self.modeman.mainwindow = self.mainwindow
@ -386,6 +388,9 @@ class Application(QApplication):
cmd.update_completion.connect(completer.on_update_completion) cmd.update_completion.connect(completer.on_update_completion)
completer.change_completed_part.connect(cmd.on_change_completed_part) completer.change_completed_part.connect(cmd.on_change_completed_part)
# downloads
tabs.start_download.connect(self.downloadmanager.fetch)
def _recover_pages(self): def _recover_pages(self):
"""Try to recover all open pages. """Try to recover all open pages.

View File

@ -0,0 +1,88 @@
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Download manager."""
import os.path
from PyQt5.QtCore import pyqtSlot, QObject
from qutebrowser.utils.log import downloads as logger
class DownloadManager(QObject):
"""Manager for running downloads."""
def __init__(self, parent=None):
super().__init__(parent)
self.downloads = []
def _get_filename(self, reply):
"""Get a suitable filename to download a file to.
Args:
reply: The QNetworkReply to get a filename for."""
filename = None
# First check if the Content-Disposition header has a filename
# attribute.
if reply.hasRawHeader('Content-Disposition'):
header = reply.rawHeader('Content-Disposition')
data = header.split(':', maxsplit=1)[1].strip()
for pair in data.split(';'):
if '=' in pair:
key, value = pair.split('=')
if key == 'filename':
filename = value.strip('"')
break
# Then try to get filename from url
if not filename:
filename = reply.url().path()
# If that fails as well, use a fallback
if not filename:
filename = 'qutebrowser-download'
return os.path.basename(filename)
@pyqtSlot('QNetworkReply')
def fetch(self, reply):
"""Download a QNetworkReply to disk.
Args:
reply: The QNetworkReply to download.
"""
filename = self._get_filename(reply)
logger.debug("fetch: {} -> {}".format(reply.url(), filename))
reply.downloadProgress.connect(self.on_download_progress)
reply.readyRead.connect(self.on_ready_read)
reply.finished.connect(self.on_finished)
@pyqtSlot(int, int)
def on_download_progress(self, done, total):
if total == -1:
perc = '???'
else:
perc = 100 * done / total
logger.debug("{}% done".format(perc))
@pyqtSlot()
def on_ready_read(self):
logger.debug("readyread")
self.sender().readAll()
@pyqtSlot()
def on_finished(self):
logger.debug("finished")

View File

@ -18,7 +18,7 @@
"""The main browser widgets.""" """The main browser widgets."""
import sip import sip
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication, pyqtSignal, pyqtSlot
from PyQt5.QtNetwork import QNetworkReply from PyQt5.QtNetwork import QNetworkReply
from PyQt5.QtWidgets import QFileDialog from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtPrintSupport import QPrintDialog from PyQt5.QtPrintSupport import QPrintDialog
@ -39,8 +39,13 @@ class BrowserPage(QWebPage):
Attributes: Attributes:
_extension_handlers: Mapping of QWebPage extensions to their handlers. _extension_handlers: Mapping of QWebPage extensions to their handlers.
network_access_manager: The QNetworkAccessManager used. network_access_manager: The QNetworkAccessManager used.
Signals:
start_download: Emitted when a file should be downloaded.
""" """
start_download = pyqtSignal('QNetworkReply*')
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self._extension_handlers = { self._extension_handlers = {
@ -50,6 +55,7 @@ class BrowserPage(QWebPage):
self.setNetworkAccessManager( self.setNetworkAccessManager(
QCoreApplication.instance().networkmanager) QCoreApplication.instance().networkmanager)
self.printRequested.connect(self.on_print_requested) self.printRequested.connect(self.on_print_requested)
self.downloadRequested.connect(self.on_download_requested)
def _handle_errorpage(self, opt, out): def _handle_errorpage(self, opt, out):
"""Display an error page if needed. """Display an error page if needed.
@ -118,6 +124,17 @@ class BrowserPage(QWebPage):
printdiag = QPrintDialog() printdiag = QPrintDialog()
printdiag.open(lambda: frame.print(printdiag.printer())) printdiag.open(lambda: frame.print(printdiag.printer()))
@pyqtSlot('QNetworkRequest')
def on_download_requested(self, request):
"""Called when the user wants to download a link.
Emit:
start_download: Emitted with the QNetworkReply associated with the
passed request.
"""
reply = self.networkAccessManager().get(request)
self.start_download.emit(reply)
def userAgentForUrl(self, url): def userAgentForUrl(self, url):
"""Override QWebPage::userAgentForUrl to customize the user agent.""" """Override QWebPage::userAgentForUrl to customize the user agent."""
ua = config.get('network', 'user-agent') ua = config.get('network', 'user-agent')

View File

@ -66,6 +66,7 @@ init = getLogger('init')
signals = getLogger('signals') signals = getLogger('signals')
hints = getLogger('hints') hints = getLogger('hints')
keyboard = getLogger('keyboard') keyboard = getLogger('keyboard')
downloads = getLogger('downloads')
js = getLogger('js') js = getLogger('js')
qt = getLogger('qt') qt = getLogger('qt')

View File

@ -72,6 +72,8 @@ class TabbedBrowser(TabWidget):
resized: Emitted when the browser window has resized, so the completion resized: Emitted when the browser window has resized, so the completion
widget can adjust its size to it. widget can adjust its size to it.
arg: The new size. arg: The new size.
start_download: Emitted when any tab wants to start downloading
something.
""" """
cur_progress = pyqtSignal(int) cur_progress = pyqtSignal(int)
@ -82,6 +84,7 @@ class TabbedBrowser(TabWidget):
cur_link_hovered = pyqtSignal(str, str, str) cur_link_hovered = pyqtSignal(str, str, str)
cur_scroll_perc_changed = pyqtSignal(int, int) cur_scroll_perc_changed = pyqtSignal(int, int)
cur_load_status_changed = pyqtSignal(str) cur_load_status_changed = pyqtSignal(str)
start_download = pyqtSignal('QNetworkReply*')
hint_strings_updated = pyqtSignal(list) hint_strings_updated = pyqtSignal(list)
shutdown_complete = pyqtSignal() shutdown_complete = pyqtSignal()
quit = pyqtSignal() quit = pyqtSignal()
@ -150,6 +153,9 @@ class TabbedBrowser(TabWidget):
# hintmanager # hintmanager
tab.hintmanager.hint_strings_updated.connect(self.hint_strings_updated) tab.hintmanager.hint_strings_updated.connect(self.hint_strings_updated)
tab.hintmanager.openurl.connect(self.cmd.openurl) tab.hintmanager.openurl.connect(self.cmd.openurl)
# downloads
tab.page().unsupportedContent.connect(self.start_download)
tab.page().start_download.connect(self.start_download)
# misc # misc
tab.titleChanged.connect(self.on_title_changed) tab.titleChanged.connect(self.on_title_changed)
tab.iconChanged.connect(self.on_icon_changed) tab.iconChanged.connect(self.on_icon_changed)