mirror of
https://github.com/lutris/lutris
synced 2024-10-06 07:50:16 +00:00
Implement download retries
This commit is contained in:
parent
b7301fd03a
commit
4dfeafa710
|
@ -2,7 +2,7 @@ from gettext import gettext as _
|
|||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from lutris.gui.widgets.download_progress import DownloadProgressBox
|
||||
from lutris.gui.widgets.download_progress_box import DownloadProgressBox
|
||||
|
||||
|
||||
class DownloadDialog(Gtk.Dialog):
|
||||
|
@ -13,15 +13,15 @@ class DownloadDialog(Gtk.Dialog):
|
|||
self.set_size_request(485, 104)
|
||||
self.set_border_width(12)
|
||||
params = {"url": url, "dest": dest, "title": label or _("Downloading %s") % url}
|
||||
self.download_box = DownloadProgressBox(params, downloader=downloader)
|
||||
self.dialog_progress_box = DownloadProgressBox(params, downloader=downloader)
|
||||
|
||||
self.download_box.connect("complete", self.download_complete)
|
||||
self.download_box.connect("cancel", self.download_cancelled)
|
||||
self.dialog_progress_box.connect("complete", self.download_complete)
|
||||
self.dialog_progress_box.connect("cancel", self.download_cancelled)
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
self.get_content_area().add(self.download_box)
|
||||
self.get_content_area().add(self.dialog_progress_box)
|
||||
self.show_all()
|
||||
self.download_box.start()
|
||||
self.dialog_progress_box.start()
|
||||
|
||||
def download_complete(self, _widget, _data):
|
||||
self.response(Gtk.ResponseType.OK)
|
||||
|
@ -33,7 +33,7 @@ class DownloadDialog(Gtk.Dialog):
|
|||
|
||||
def on_response(self, _dialog, response):
|
||||
if response == Gtk.ResponseType.DELETE_EVENT:
|
||||
self.download_box.downloader.cancel()
|
||||
self.dialog_progress_box.downloader.cancel()
|
||||
self.destroy()
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ from gi.repository import GObject, Gtk
|
|||
from lutris.cache import save_to_cache
|
||||
from lutris.gui.installer.widgets import InstallerLabel
|
||||
from lutris.gui.widgets.common import FileChooserEntry
|
||||
from lutris.gui.widgets.download_progress import DownloadProgressBox
|
||||
from lutris.gui.widgets.download_progress_box import DownloadProgressBox
|
||||
from lutris.installer.steam_installer import SteamInstaller
|
||||
from lutris.util import system
|
||||
from lutris.util.log import logger
|
||||
from lutris.util.strings import add_url_tags, gtk_safe
|
||||
|
||||
|
||||
|
@ -54,8 +55,9 @@ class InstallerFileBox(Gtk.VBox):
|
|||
"url": self.installer_file.url,
|
||||
"dest": self.installer_file.dest_file,
|
||||
"referer": self.installer_file.referer
|
||||
}, cancelable=True)
|
||||
})
|
||||
download_progress.connect("complete", self.on_download_complete)
|
||||
download_progress.connect("cancel", self.on_download_cancelled)
|
||||
download_progress.show()
|
||||
if (
|
||||
not self.installer_file.uses_pga_cache()
|
||||
|
@ -70,7 +72,7 @@ class InstallerFileBox(Gtk.VBox):
|
|||
if self.provider == "download":
|
||||
download_progress = self.get_download_progress()
|
||||
self.start_func = download_progress.start
|
||||
self.stop_func = download_progress.cancel
|
||||
self.stop_func = download_progress.on_cancel_clicked
|
||||
box.pack_start(download_progress, False, False, 0)
|
||||
return box
|
||||
if self.provider == "pga":
|
||||
|
@ -268,8 +270,10 @@ class InstallerFileBox(Gtk.VBox):
|
|||
if self.cache_to_pga:
|
||||
save_to_cache(self.installer_file.dest_file, self.installer_file.cache_path)
|
||||
|
||||
def on_download_cancelled(self):
|
||||
def on_download_cancelled(self, downloader):
|
||||
"""Handle cancellation of installers"""
|
||||
logger.error("Download from %s cancelled", downloader)
|
||||
downloader.set_retry_button()
|
||||
|
||||
def on_download_complete(self, widget, _data=None):
|
||||
"""Action called on a completed download."""
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from gettext import gettext as _
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from gi.repository import GLib, GObject, Gtk, Pango
|
||||
|
||||
from lutris.util.downloader import Downloader
|
||||
from lutris.util.log import logger
|
||||
from lutris.util.strings import gtk_safe
|
||||
|
||||
|
||||
|
@ -12,7 +14,7 @@ class DownloadProgressBox(Gtk.Box):
|
|||
|
||||
__gsignals__ = {
|
||||
"complete": (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_PYOBJECT, )),
|
||||
"cancel": (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_PYOBJECT, )),
|
||||
"cancel": (GObject.SignalFlags.RUN_LAST, None, ()),
|
||||
"error": (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_PYOBJECT, )),
|
||||
}
|
||||
|
||||
|
@ -24,9 +26,8 @@ class DownloadProgressBox(Gtk.Box):
|
|||
self.url = params.get("url")
|
||||
self.dest = params.get("dest")
|
||||
self.referer = params.get("referer")
|
||||
title = params.get("title", _("Downloading {}").format(self.url))
|
||||
|
||||
self.main_label = Gtk.Label(title)
|
||||
self.main_label = Gtk.Label(self.get_title())
|
||||
self.main_label.set_alignment(0, 0)
|
||||
self.main_label.set_property("wrap", True)
|
||||
self.main_label.set_margin_bottom(10)
|
||||
|
@ -44,7 +45,7 @@ class DownloadProgressBox(Gtk.Box):
|
|||
progress_box.pack_start(self.progressbar, True, True, 0)
|
||||
|
||||
self.cancel_button = Gtk.Button.new_with_mnemonic(_("_Cancel"))
|
||||
self.cancel_button.connect("clicked", self.cancel)
|
||||
self.cancel_cb_id = self.cancel_button.connect("clicked", self.on_cancel_clicked)
|
||||
if not cancelable:
|
||||
self.cancel_button.set_sensitive(False)
|
||||
progress_box.pack_end(self.cancel_button, False, False, 0)
|
||||
|
@ -58,6 +59,11 @@ class DownloadProgressBox(Gtk.Box):
|
|||
self.show_all()
|
||||
self.cancel_button.hide()
|
||||
|
||||
def get_title(self):
|
||||
"""Return the main label text for the widget"""
|
||||
parsed = urlparse(self.url)
|
||||
return "%s%s" % (parsed.netloc, parsed.path)
|
||||
|
||||
def start(self):
|
||||
"""Start downloading a file."""
|
||||
if not self.downloader:
|
||||
|
@ -67,7 +73,7 @@ class DownloadProgressBox(Gtk.Box):
|
|||
from lutris.gui.dialogs import ErrorDialog
|
||||
|
||||
ErrorDialog(ex.args[0])
|
||||
self.emit("cancel", {})
|
||||
self.emit("cancel")
|
||||
return None
|
||||
|
||||
timer_id = GLib.timeout_add(500, self._progress)
|
||||
|
@ -77,12 +83,28 @@ class DownloadProgressBox(Gtk.Box):
|
|||
self.downloader.start()
|
||||
return timer_id
|
||||
|
||||
def cancel(self, _widget=None):
|
||||
def set_retry_button(self):
|
||||
"""Transform the cancel button into a retry button"""
|
||||
self.cancel_button.set_label(_("Retry"))
|
||||
self.cancel_button.disconnect(self.cancel_cb_id)
|
||||
self.cancel_cb_id = self.cancel_button.connect("clicked", self.on_retry_clicked)
|
||||
self.cancel_button.set_sensitive(True)
|
||||
|
||||
def on_retry_clicked(self, button):
|
||||
logger.debug("Retrying download")
|
||||
button.set_label(_("Cancel"))
|
||||
button.disconnect(self.cancel_cb_id)
|
||||
self.cancel_cb_id = button.connect("clicked", self.on_cancel_clicked)
|
||||
self.downloader.reset()
|
||||
self.start()
|
||||
|
||||
def on_cancel_clicked(self, _widget=None):
|
||||
"""Cancel the current download."""
|
||||
logger.debug("Download cancel requested")
|
||||
if self.downloader:
|
||||
self.downloader.cancel()
|
||||
self.cancel_button.set_sensitive(False)
|
||||
self.emit("cancel", {})
|
||||
self.emit("cancel")
|
||||
|
||||
def _progress(self):
|
||||
"""Show download progress."""
|
||||
|
@ -91,10 +113,9 @@ class DownloadProgressBox(Gtk.Box):
|
|||
self.progressbar.set_fraction(0)
|
||||
if self.downloader.state == self.downloader.CANCELLED:
|
||||
self._set_text(_("Download interrupted"))
|
||||
self.emit("cancel")
|
||||
else:
|
||||
self._set_text(str(self.downloader.error)[:80])
|
||||
if self.downloader.state == self.downloader.CANCELLED:
|
||||
self.emit("cancel", {})
|
||||
return False
|
||||
self.progressbar.set_fraction(progress)
|
||||
megabytes = 1024 * 1024
|
|
@ -23,7 +23,13 @@ class Downloader:
|
|||
Stop with cancel().
|
||||
"""
|
||||
|
||||
(INIT, DOWNLOADING, CANCELLED, ERROR, COMPLETED) = list(range(5))
|
||||
(
|
||||
INIT,
|
||||
DOWNLOADING,
|
||||
CANCELLED,
|
||||
ERROR,
|
||||
COMPLETED
|
||||
) = list(range(5))
|
||||
|
||||
def __init__(self, url, dest, overwrite=False, referer=None, callback=None):
|
||||
self.url = url
|
||||
|
@ -44,7 +50,6 @@ class Downloader:
|
|||
self.speed = 0
|
||||
self.average_speed = 0
|
||||
self.time_left = "00:00:00" # Based on average speed
|
||||
|
||||
self.last_size = 0
|
||||
self.last_check_time = 0
|
||||
self.last_speeds = []
|
||||
|
@ -52,9 +57,12 @@ class Downloader:
|
|||
self.time_left_check_time = 0
|
||||
self.file_pointer = None
|
||||
|
||||
def __str__(self):
|
||||
return "downloader for %s" % self.url
|
||||
|
||||
def start(self):
|
||||
"""Start download job."""
|
||||
logger.debug("Starting download of:\n %s", self.url)
|
||||
logger.debug("⬇ %s", self.url)
|
||||
self.state = self.DOWNLOADING
|
||||
self.last_check_time = get_time()
|
||||
if self.overwrite and os.path.isfile(self.dest):
|
||||
|
@ -63,6 +71,24 @@ class Downloader:
|
|||
self.thread = jobs.AsyncCall(self.async_download, self.download_cb)
|
||||
self.stop_request = self.thread.stop_request
|
||||
|
||||
def reset(self):
|
||||
"""Reset the state of the downloader"""
|
||||
self.state = self.INIT
|
||||
self.error = None
|
||||
self.downloaded_size = 0 # Bytes
|
||||
self.full_size = 0 # Bytes
|
||||
self.progress_fraction = 0
|
||||
self.progress_percentage = 0
|
||||
self.speed = 0
|
||||
self.average_speed = 0
|
||||
self.time_left = "00:00:00" # Based on average speed
|
||||
self.last_size = 0
|
||||
self.last_check_time = 0
|
||||
self.last_speeds = []
|
||||
self.speed_check_time = 0
|
||||
self.time_left_check_time = 0
|
||||
self.file_pointer = None
|
||||
|
||||
def check_progress(self):
|
||||
"""Append last downloaded chunk to dest file and store stats.
|
||||
|
||||
|
@ -73,7 +99,7 @@ class Downloader:
|
|||
|
||||
def cancel(self):
|
||||
"""Request download stop and remove destination file."""
|
||||
logger.debug("Download of %s cancelled", self.url)
|
||||
logger.debug("❌ %s", self.url)
|
||||
self.state = self.CANCELLED
|
||||
if self.stop_request:
|
||||
self.stop_request.set()
|
||||
|
|
|
@ -236,7 +236,7 @@ class WinePrefixManager:
|
|||
"""Disables some joypad devices"""
|
||||
key = self.hkcu_prefix + "/Software/Wine/DirectInput/Joysticks"
|
||||
self.clear_registry_key(key)
|
||||
for device, joypad_name in joypad.get_joypads():
|
||||
for _device, joypad_name in joypad.get_joypads():
|
||||
# Attempt at disabling mice that register as joysticks.
|
||||
# Although, those devices aren't returned by `get_joypads`
|
||||
# A better way would be to read /dev/input files directly.
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
"""Manipulate Wine registry files"""
|
||||
# Standard Library
|
||||
import os
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
|
||||
# Lutris Modules
|
||||
from lutris.util import system
|
||||
from lutris.util.log import logger
|
||||
from lutris.util.wine.wine import WINE_DEFAULT_ARCH
|
||||
|
|
Loading…
Reference in a new issue