Move most file-collection specific code to virtual methods.

This is to separate out all uses of 'dest_file', so it can be treated specially by the file collection
This commit is contained in:
Daniel Johnson 2023-09-17 14:10:27 -04:00
parent 55008b8b69
commit ea278e8b2c
5 changed files with 99 additions and 66 deletions

View file

@ -1,6 +1,2 @@
class NotInstallerFile(Exception):
"""Error that represents a installer file type that is not supported"""
class UnsupportedProvider(Exception):
"""Error that represents a unsupported provider"""
"""Error that represents an unsupported provider"""

View file

@ -4,13 +4,9 @@ from gettext import gettext as _
from gi.repository import GObject, Gtk
from lutris.cache import save_to_cache
from lutris.gui.installer import NotInstallerFile, UnsupportedProvider
from lutris.gui.installer import UnsupportedProvider
from lutris.gui.installer.widgets import InstallerLabel
from lutris.gui.widgets.common import FileChooserEntry
from lutris.gui.widgets.download_collection_progress_box import DownloadCollectionProgressBox
from lutris.gui.widgets.download_progress_box import DownloadProgressBox
from lutris.installer.installer_file import InstallerFile
from lutris.installer.installer_file_collection import InstallerFileCollection
from lutris.installer.steam_installer import SteamInstaller
from lutris.util import system
@ -37,49 +33,22 @@ class InstallerFileBox(Gtk.VBox):
self.state_label = None # Use this label to display status update
self.set_margin_left(12)
self.set_margin_right(12)
self.provider = self.installer_file.provider
self.provider = self.installer_file.default_provider
self.file_provider_widget = None
self.add(self.get_widgets())
@property
def is_ready(self):
"""Whether the file is ready to be downloaded / fetched from its provider"""
if (
self.provider in ("user", "pga")
and not system.path_exists(self.installer_file.dest_file)
):
return False
return True
return self.installer_file.is_ready(self.provider)
def get_download_progress(self):
"""Return the widget for the download progress bar"""
download_progress = None
if isinstance(self.installer_file, InstallerFile):
download_progress = DownloadProgressBox({
"url": self.installer_file.url,
"dest": self.installer_file.dest_file,
"referer": self.installer_file.referer
}, downloader=self.installer_file.downloader)
elif isinstance(self.installer_file, InstallerFileCollection):
download_progress = DownloadCollectionProgressBox(self.installer_file)
else:
raise NotInstallerFile(
"Installer file isn't type InstallerFile or InstallerFileCollection\n{}"
.format(self.installer_file))
download_progress = self.installer_file.create_download_progress_box()
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()
and system.path_exists(self.installer_file.dest_file)
):
# If we've previously downloaded a directory, we'll need to get rid of it
# to download a file now. Since we are not using the cache, we don't keep
# these files anyway - so it should be safe to just nuke and pave all this.
if os.path.isdir(self.installer_file.dest_file):
system.remove_folder(self.installer_file.dest_file)
else:
os.remove(self.installer_file.dest_file)
self.installer_file.remove_previous()
return download_progress
def get_file_provider_widget(self):
@ -212,8 +181,10 @@ class InstallerFileBox(Gtk.VBox):
source_box = Gtk.HBox()
source_box.props.valign = Gtk.Align.START
box.pack_start(source_box, False, False, 0)
if isinstance(self.installer_file, InstallerFileCollection):
source_box.pack_start(InstallerLabel(f"{self.installer_file.num_files} {_('Files')}"), False, False, 0)
aux_info = self.installer_file.auxiliary_info
if aux_info:
source_box.pack_start(InstallerLabel(aux_info), False, False, 0)
source_box.pack_start(InstallerLabel(_("Source:")), False, False, 0)
combobox = self.get_combobox()
source_box.pack_start(combobox, False, False, 0)
@ -251,11 +222,7 @@ class InstallerFileBox(Gtk.VBox):
def cache_file(self):
"""Copy file to the PGA cache"""
if self.cache_to_pga:
if isinstance(self.installer_file, InstallerFile):
save_to_cache(self.installer_file.dest_file, self.installer_file.cache_path)
elif isinstance(self.installer_file, InstallerFileCollection):
for installer_file in self.installer_file.files_list:
save_to_cache(installer_file.dest_file, installer_file.cache_path)
self.installer_file.save_to_cache()
def on_download_cancelled(self, downloader):
"""Handle cancellation of installers"""

View file

@ -1,7 +1,6 @@
from gi.repository import GObject, Gtk
from lutris.gui.installer.file_box import InstallerFileBox
from lutris.installer.installer_file_collection import InstallerFileCollection
from lutris.util.log import logger
@ -117,11 +116,7 @@ class InstallerFilesBox(Gtk.ListBox):
def get_game_files(self):
"""Return a mapping of the local files usable by the interpreter"""
out = {}
files = {}
for installer_file in self.installer.files:
if isinstance(installer_file, InstallerFileCollection):
for file in installer_file.files_list:
out.update({file.id: file.dest_file})
else:
out.update({installer_file.id: installer_file.dest_file})
return out
files.update(installer_file.get_dest_files_by_id())
return files

View file

@ -4,6 +4,8 @@ from gettext import gettext as _
from urllib.parse import urlparse
from lutris import cache, settings
from lutris.cache import save_to_cache
from lutris.gui.widgets.download_progress_box import DownloadProgressBox
from lutris.installer.errors import ScriptingError
from lutris.util import system
from lutris.util.log import logger
@ -88,12 +90,20 @@ class InstallerFile:
def dest_file(self, value):
self._dest_file = value
def get_dest_files_by_id(self):
return {self.id: self.dest_file}
def __str__(self):
return "%s/%s" % (self.game_slug, self.id)
@property
def auxiliary_info(self):
"""Provides a small bit of additional descriptive texts to show in the UI."""
return None
@property
def human_url(self):
"""Return the url in human readable format"""
"""Return the url in human-readable format"""
if self.url.startswith("N/A"):
# Ask the user where the file is located
parts = self.url.split(":", 1)
@ -123,7 +133,7 @@ class InstallerFile:
return ""
@property
def provider(self):
def default_provider(self):
"""Return file provider used"""
if self.url.startswith("$STEAM"):
return "steam"
@ -195,6 +205,13 @@ class InstallerFile:
if not system.path_exists(self.cache_path):
os.makedirs(self.cache_path)
def create_download_progress_box(self):
return DownloadProgressBox({
"url": self.url,
"dest": self.dest_file,
"referer": self.referer
}, downloader=self.downloader)
def check_hash(self):
"""Checks the checksum of `file` and compare it to `value`
@ -219,7 +236,29 @@ class InstallerFile:
return self._file_meta["size"]
return 0
def is_ready(self, provider):
"""Is the file already present at the destination (if applicable)?"""
return provider not in ("user", "pga") or system.path_exists(self.dest_file)
@property
def is_cached(self):
"""Is the file available in the local PGA cache?"""
return self.uses_pga_cache() and system.path_exists(self.dest_file)
def save_to_cache(self):
"""Copy the file into the PGA cache."""
save_to_cache(self.dest_file, self.cache_path)
def remove_previous(self):
"""Remove file at already at destination, prior to starting the download."""
if (
not self.uses_pga_cache()
and system.path_exists(self.dest_file)
):
# If we've previously downloaded a directory, we'll need to get rid of it
# to download a file now. Since we are not using the cache, we don't keep
# these files anyway - so it should be safe to just nuke and pave all this.
if os.path.isdir(self.dest_file):
system.remove_folder(self.dest_file)
else:
os.remove(self.dest_file)

View file

@ -2,8 +2,10 @@
import os
from functools import reduce
from urllib.parse import urlparse
from gettext import gettext as _
from lutris import cache, settings
from lutris.gui.widgets.download_collection_progress_box import DownloadCollectionProgressBox
from lutris.util import system
from lutris.util.log import logger
from lutris.util.strings import add_url_tags, gtk_safe
@ -15,12 +17,12 @@ class InstallerFileCollection:
"""Representation of a collection of files in the `files` sections of an installer.
Store files in a folder"""
def __init__(self, game_slug, file_id, files_list, dest_folder=None):
def __init__(self, game_slug, file_id, files_list, dest_file=None):
self.game_slug = game_slug
self.id = file_id.replace("-", "_") # pylint: disable=invalid-name
self.num_files = len(files_list)
self.files_list = files_list
self._dest_folder = dest_folder # Used to override the destination
self._dest_file = dest_file # Used to override the destination
self.full_size = 0
self._get_files_size()
self._get_service()
@ -50,21 +52,35 @@ class InstallerFileCollection:
@property
def dest_file(self):
"""dest_file represents destination folder to all file collection"""
if self._dest_folder:
return self._dest_folder
if self._dest_file:
return self._dest_file
return self.cache_path
@dest_file.setter
def dest_file(self, value):
self._dest_folder = value
self._dest_file = value
# try to set main gog file to dest_file
for installer_file in self.files_list:
if installer_file.id == "goginstaller":
installer_file.dest_file = value
def get_dest_files_by_id(self):
files = {}
for file in self.files_list:
files.update({file.id: file.dest_file})
return files
def __str__(self):
return "%s/%s" % (self.game_slug, self.id)
@property
def auxiliary_info(self):
"""Provides a small bit of additional descriptive texts to show in the UI."""
if self.num_files == 1:
return f"{self.num_files} {_('File')}"
return f"{self.num_files} {_('Files')}"
@property
def human_url(self):
"""Return game_slug"""
@ -75,7 +91,7 @@ class InstallerFileCollection:
return add_url_tags(gtk_safe(self.game_slug))
@property
def provider(self):
def default_provider(self):
"""Return file provider used. File Collection only supports 'pga' and 'download'"""
if self.is_cached:
return "pga"
@ -127,8 +143,18 @@ class InstallerFileCollection:
def prepare(self):
"""Prepare all files for download"""
# File Collection do not need to prepare, only the files_list
for file in self.files_list:
file.prepare()
for installer_file in self.files_list:
installer_file.prepare()
def create_download_progress_box(self):
return DownloadCollectionProgressBox(self)
def is_ready(self, provider):
"""Are all the files already present at the destination?"""
for installer_file in self.files_list:
if not installer_file.is_ready(provider):
return False
return True
@property
def is_cached(self):
@ -140,3 +166,13 @@ class InstallerFileCollection:
return False
return True
return False
def save_to_cache(self):
"""Copy the files into the PGA cache."""
for installer_file in self.files_list:
installer_file.save_to_cache()
def remove_previous(self):
"""Remove file at already at destination, prior to starting the download."""
for installer_file in self.files_list:
installer_file.remove_previous()