From a780325a3ae279785a1f6b9c020c2b5cde925c46 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 19 Oct 2015 20:08:37 +0200 Subject: [PATCH] Allow directories to be entered as destination The filename will then default to 'page title.mht' --- qutebrowser/browser/downloads.py | 63 +++++++++++++++++--------------- qutebrowser/browser/mhtml.py | 41 +++++++++++++++------ 2 files changed, 63 insertions(+), 41 deletions(-) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 3478b0a87..efe7f21e9 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -49,7 +49,7 @@ ModelRole = usertypes.enum('ModelRole', ['item'], start=Qt.UserRole, RetryInfo = collections.namedtuple('RetryInfo', ['request', 'manager']) # Remember the last used directory -_last_used_directory = None +last_used_directory = None # All REFRESH_INTERVAL milliseconds, speeds will be recalculated and downloads @@ -57,13 +57,13 @@ _last_used_directory = None REFRESH_INTERVAL = 500 -def _download_dir(): +def download_dir(): """Get the download directory to use.""" directory = config.get('storage', 'download-directory') remember_dir = config.get('storage', 'remember-download-directory') - if remember_dir and _last_used_directory is not None: - return _last_used_directory + if remember_dir and last_used_directory is not None: + return last_used_directory elif directory is None: return standarddir.download() else: @@ -79,15 +79,36 @@ def path_suggestion(filename): suggestion = config.get('completion', 'download-path-suggestion') if suggestion == 'path': # add trailing '/' if not present - return os.path.join(_download_dir(), '') + return os.path.join(download_dir(), '') elif suggestion == 'filename': return filename elif suggestion == 'both': - return os.path.join(_download_dir(), filename) + return os.path.join(download_dir(), filename) else: raise ValueError("Invalid suggestion value {}!".format(suggestion)) +def create_full_filename(basename, filename): + """Create a full filename based on the given basename and filename. + + Args: + basename: The basename to use if filename is a directory. + filename: The path to a folder or file where you want to save. + + Return: + The full absolute path, or None if filename creation was not possible. + """ + if os.path.isabs(filename) and os.path.isdir(filename): + # We got an absolute directory from the user, so we save it under + # the default filename in that directory. + return os.path.join(filename, basename) + elif os.path.isabs(filename): + # We got an absolute filename from the user, so we save it under + # that filename. + return filename + return None + + class DownloadItemStats(QObject): """Statistics (bytes done, total bytes, time, etc.) about a download. @@ -447,7 +468,7 @@ class DownloadItem(QObject): filename: The full filename to save the download to. None: special value to stop the download. """ - global _last_used_directory + global last_used_directory if self.fileobj is not None: raise ValueError("fileobj was already set! filename: {}, " "existing: {}, fileobj {}".format( @@ -457,13 +478,16 @@ class DownloadItem(QObject): # See https://github.com/The-Compiler/qutebrowser/issues/427 encoding = sys.getfilesystemencoding() filename = utils.force_encoding(filename, encoding) - if not self._create_full_filename(filename): + self._filename = create_full_filename(self.basename, filename) + if self._filename is None: # We only got a filename (without directory) or a relative path # from the user, so we append that to the default directory and # try again. - self._create_full_filename(os.path.join(_download_dir(), filename)) + self._filename = create_full_filename( + self.basename, os.path.join(download_dir(), filename)) - _last_used_directory = os.path.dirname(self._filename) + self.basename = os.path.basename(self._filename) + last_used_directory = os.path.dirname(self._filename) log.downloads.debug("Setting filename to {}".format(filename)) if os.path.isfile(self._filename): @@ -480,25 +504,6 @@ class DownloadItem(QObject): else: self._create_fileobj() - def _create_full_filename(self, filename): - """Try to create the full filename. - - Return: - True if the full filename was created, False otherwise. - """ - if os.path.isabs(filename) and os.path.isdir(filename): - # We got an absolute directory from the user, so we save it under - # the default filename in that directory. - self._filename = os.path.join(filename, self.basename) - return True - elif os.path.isabs(filename): - # We got an absolute filename from the user, so we save it under - # that filename. - self._filename = filename - self.basename = os.path.basename(self._filename) - return True - return False - def set_fileobj(self, fileobj): """"Set the file object to write the download to. diff --git a/qutebrowser/browser/mhtml.py b/qutebrowser/browser/mhtml.py index 82bb20ed0..23170f621 100644 --- a/qutebrowser/browser/mhtml.py +++ b/qutebrowser/browser/mhtml.py @@ -23,6 +23,7 @@ import functools import io import os import re +import sys import collections import uuid import email.policy @@ -32,7 +33,7 @@ import email.mime.multipart from PyQt5.QtCore import QUrl -from qutebrowser.browser import webelem +from qutebrowser.browser import webelem, downloads from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils try: @@ -373,7 +374,7 @@ class _Downloader(): log.downloads.debug("Oops! Download already gone: %s", item) return item.fileobj.actual_close() - self.writer.add_file(ulrutils.encoded_url(url), b'') + self.writer.add_file(urlutils.encoded_url(url), b'') if self.pending_downloads: return self.finish_file() @@ -428,12 +429,11 @@ def _start_download(dest, win_id, tab_id): dest: The filename where the resulting file should be saved. win_id, tab_id: Specify the tab whose page should be loaded. """ - - dest = os.path.expanduser(dest) web_view = objreg.get('webview', scope='tab', window=win_id, tab=tab_id) loader = _Downloader(web_view, dest) loader.run() + def start_download_checked(dest, win_id, tab_id): """First check if dest is already a file, then start the download. @@ -441,20 +441,37 @@ def start_download_checked(dest, win_id, tab_id): dest: The filename where the resulting file should be saved. win_id, tab_id: Specify the tab whose page should be loaded. """ - # start_download will call os.path.expanduser on dest too, so no need to - # overwrite dest. We just want to make sure that we're checking - # the right path here. This also means that the user question will show the - # original path, not the expanded. - if not os.path.isfile(os.path.expanduser(dest)): - _start_download(dest, win_id=win_id, tab_id=tab_id) + # The default name is 'page title.mht' + title = (objreg.get('webview', scope='tab', window=win_id, tab=tab_id) + .title()) + default_name = title + '.mht' + + # Remove characters which cannot be expressed in the file system encoding + encoding = sys.getfilesystemencoding() + default_name = utils.force_encoding(default_name, encoding) + dest = utils.force_encoding(dest, encoding) + + dest = os.path.expanduser(dest) + + # See if we already have an absolute path + path = downloads.create_full_filename(default_name, dest) + if path is None: + # We still only have a relative path, prepend download_dir and + # try again. + path = downloads.create_full_filename( + default_name, os.path.join(downloads.download_dir(), dest)) + downloads.last_used_directory = os.path.dirname(path) + + if not os.path.isfile(path): + _start_download(path, win_id=win_id, tab_id=tab_id) return q = usertypes.Question() q.mode = usertypes.PromptMode.yesno - q.text = "{} exists. Overwrite?".format(dest) + q.text = "{} exists. Overwrite?".format(path) q.completed.connect(q.deleteLater) q.answered_yes.connect(functools.partial( - _start_download, dest, win_id=win_id, tab_id=tab_id)) + _start_download, path, win_id=win_id, tab_id=tab_id)) message_bridge = objreg.get('message-bridge', scope='window', window=win_id) message_bridge.ask(q, blocking=False)