mirror of
https://github.com/lutris/lutris
synced 2024-10-14 19:53:53 +00:00
Download missing Lutris media when installing games during service refresh.
This extends sync_media() so it can download for a specific set of games, so we can download only the installed games, not games you had already. Resolves #4545
This commit is contained in:
parent
8cabab49c7
commit
a903b67180
|
@ -11,6 +11,7 @@ from lutris.database.games import add_game, get_game_by_field
|
|||
from lutris.database.services import ServiceGameCollection
|
||||
from lutris.game import Game
|
||||
from lutris.services.base import BaseService
|
||||
from lutris.services.lutris import sync_media
|
||||
from lutris.services.service_game import ServiceGame
|
||||
from lutris.services.service_media import ServiceMedia
|
||||
from lutris.util.battlenet.definitions import ProductDbInfo
|
||||
|
@ -18,12 +19,12 @@ from lutris.util.log import logger
|
|||
|
||||
try:
|
||||
from lutris.util.battlenet.product_db_pb2 import ProductDb
|
||||
|
||||
BNET_ENABLED = True
|
||||
except (ImportError, TypeError) as ex:
|
||||
logger.warning("The Battle.net source is unavailable because Google protobuf could not be loaded: %s", ex)
|
||||
BNET_ENABLED = False
|
||||
|
||||
|
||||
GAME_IDS = {
|
||||
's1': ('s1', 'StarCraft', 'S1', 'starcraft-remastered'),
|
||||
's2': ('s2', 'StarCraft II', 'S2', 'starcraft-ii'),
|
||||
|
@ -118,8 +119,12 @@ class BattleNetService(BaseService):
|
|||
raise RuntimeError("Battle.net is not installed in Lutris")
|
||||
bnet_prefix = bnet_game["directory"].split("drive_c")[0]
|
||||
parser = BlizzardProductDbParser(bnet_prefix)
|
||||
installed_slugs = []
|
||||
for game in parser.games:
|
||||
self.install_from_battlenet(bnet_game, game)
|
||||
slug = self.install_from_battlenet(bnet_game, game)
|
||||
if slug:
|
||||
installed_slugs.append(slug)
|
||||
sync_media(installed_slugs)
|
||||
|
||||
def install_from_battlenet(self, bnet_game, game):
|
||||
app_id = game.ngdp
|
||||
|
@ -135,10 +140,11 @@ class BattleNetService(BaseService):
|
|||
game_config = LutrisConfig(game_config_id=bnet_game["configpath"]).game_level
|
||||
game_config["game"]["args"] = '--exec="launch %s"' % game.ngdp
|
||||
configpath = write_game_config(lutris_game_id, game_config)
|
||||
game_id = add_game(
|
||||
slug = self.get_installed_slug(bnet_game)
|
||||
add_game(
|
||||
name=service_game["name"],
|
||||
runner=bnet_game["runner"],
|
||||
slug=service_game["slug"],
|
||||
slug=slug,
|
||||
directory=bnet_game["directory"],
|
||||
installed=1,
|
||||
installer_slug=lutris_game_id,
|
||||
|
@ -147,7 +153,7 @@ class BattleNetService(BaseService):
|
|||
service_id=app_id,
|
||||
platform="Windows"
|
||||
)
|
||||
return game_id
|
||||
return slug
|
||||
|
||||
def get_installed_slug(self, db_game):
|
||||
return db_game.get("lutris_slug") or db_game["slug"]
|
||||
|
|
|
@ -17,6 +17,7 @@ from lutris.database.services import ServiceGameCollection
|
|||
from lutris.game import Game
|
||||
from lutris.installer import get_installers
|
||||
from lutris.services.base import OnlineService
|
||||
from lutris.services.lutris import sync_media
|
||||
from lutris.services.service_game import ServiceGame
|
||||
from lutris.services.service_media import ServiceMedia
|
||||
from lutris.util.log import logger
|
||||
|
@ -313,14 +314,15 @@ class EAAppService(OnlineService):
|
|||
logger.error("Invalid install of EA App at %s", ea_app_prefix)
|
||||
return
|
||||
ea_app_launcher = EAAppGames(ea_app_prefix)
|
||||
installed_games = 0
|
||||
installed_slugs = []
|
||||
for content_ids in ea_app_launcher.get_installed_games_content_ids():
|
||||
self.install_from_ea_app(ea_app_game, content_ids)
|
||||
installed_games += 1
|
||||
logger.debug("Installed %s EA games", installed_games)
|
||||
slug = self.install_from_ea_app(ea_app_game, content_ids)
|
||||
if slug:
|
||||
installed_slugs.append(slug)
|
||||
sync_media(installed_slugs)
|
||||
logger.debug("Installed %s EA games", len(installed_slugs))
|
||||
|
||||
def install_from_ea_app(self, ea_game, content_ids):
|
||||
|
||||
offer_id = content_ids[0]
|
||||
logger.debug("Installing EA game %s", offer_id)
|
||||
service_game = ServiceGameCollection.get_game("ea_app", offer_id)
|
||||
|
@ -334,10 +336,11 @@ class EAAppService(OnlineService):
|
|||
game_config = LutrisConfig(game_config_id=ea_game["configpath"]).game_level
|
||||
game_config["game"]["args"] = get_launch_arguments(",".join(content_ids))
|
||||
configpath = write_game_config(lutris_game_id, game_config)
|
||||
game_id = add_game(
|
||||
slug = self.get_installed_slug(ea_game)
|
||||
add_game(
|
||||
name=service_game["name"],
|
||||
runner=ea_game["runner"],
|
||||
slug=slugify(service_game["name"]),
|
||||
slug=slug,
|
||||
directory=ea_game["directory"],
|
||||
installed=1,
|
||||
installer_slug=lutris_game_id,
|
||||
|
@ -345,7 +348,7 @@ class EAAppService(OnlineService):
|
|||
service=self.id,
|
||||
service_id=offer_id,
|
||||
)
|
||||
return game_id
|
||||
return slug
|
||||
|
||||
def generate_installer(self, db_game, ea_db_game):
|
||||
ea_game = Game(ea_db_game["id"])
|
||||
|
|
|
@ -14,6 +14,7 @@ from lutris.game import Game
|
|||
from lutris.gui.widgets.utils import Image, paste_overlay, thumbnail_image
|
||||
from lutris.installer import get_installers
|
||||
from lutris.services.base import AuthTokenExpiredError, OnlineService
|
||||
from lutris.services.lutris import sync_media
|
||||
from lutris.services.service_game import ServiceGame
|
||||
from lutris.services.service_media import ServiceMedia
|
||||
from lutris.util import system
|
||||
|
@ -336,10 +337,11 @@ class EpicGamesStoreService(OnlineService):
|
|||
game_config = LutrisConfig(game_config_id=egs_game["configpath"]).game_level
|
||||
game_config["game"]["args"] = get_launch_arguments(app_name)
|
||||
configpath = write_game_config(lutris_game_id, game_config)
|
||||
game_id = add_game(
|
||||
slug = self.get_installed_slug(egs_game)
|
||||
add_game(
|
||||
name=service_game["name"],
|
||||
runner=egs_game["runner"],
|
||||
slug=slugify(service_game["name"]),
|
||||
slug=slug,
|
||||
directory=egs_game["directory"],
|
||||
installed=1,
|
||||
installer_slug=lutris_game_id,
|
||||
|
@ -347,7 +349,7 @@ class EpicGamesStoreService(OnlineService):
|
|||
service=self.id,
|
||||
service_id=app_name,
|
||||
)
|
||||
return game_id
|
||||
return slug
|
||||
|
||||
def add_installed_games(self):
|
||||
"""Scan an existing EGS install for games"""
|
||||
|
@ -362,8 +364,12 @@ class EpicGamesStoreService(OnlineService):
|
|||
logger.error("Invalid install of EGS at %s", egs_prefix)
|
||||
return
|
||||
egs_launcher = EGSLauncher(egs_prefix)
|
||||
installed_slugs = []
|
||||
for manifest in egs_launcher.iter_manifests():
|
||||
self.install_from_egs(egs_game, manifest)
|
||||
slug = self.install_from_egs(egs_game, manifest)
|
||||
if slug:
|
||||
installed_slugs.append(slug)
|
||||
sync_media(installed_slugs)
|
||||
logger.debug("All EGS games imported")
|
||||
|
||||
def generate_installer(self, db_game, egs_db_game):
|
||||
|
|
|
@ -182,13 +182,13 @@ class FlathubService(BaseService):
|
|||
"installer": [
|
||||
{
|
||||
"execute":
|
||||
{
|
||||
"file": flatpak_cmd[0],
|
||||
"args": " ".join(flatpak_cmd[1:])
|
||||
+ f" install --{self.install_type} --app --noninteractive flathub "
|
||||
f"app/{db_game['appid']}/{self.arch}/{self.branch}",
|
||||
"disable_runtime": True
|
||||
}
|
||||
{
|
||||
"file": flatpak_cmd[0],
|
||||
"args": " ".join(flatpak_cmd[1:])
|
||||
+ f" install --{self.install_type} --app --noninteractive flathub "
|
||||
f"app/{db_game['appid']}/{self.arch}/{self.branch}",
|
||||
"disable_runtime": True
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import json
|
|||
import os
|
||||
import urllib.parse
|
||||
from gettext import gettext as _
|
||||
from typing import List
|
||||
from typing import Dict, Iterable, List
|
||||
|
||||
from gi.repository import Gio
|
||||
|
||||
|
@ -180,8 +180,17 @@ def download_lutris_media(slug):
|
|||
download_media({slug: cover_url}, LutrisCoverart())
|
||||
|
||||
|
||||
def sync_media() -> dict:
|
||||
"""Downlad all missing media"""
|
||||
def sync_media(slugs: Iterable[str] = None) -> Dict[str, int]:
|
||||
"""Download missing media for Lutris games; if a set of slugs
|
||||
is not provided, downloads them for all games in the PGA."""
|
||||
if slugs is None:
|
||||
slugs = {game["slug"] for game in get_games()}
|
||||
else:
|
||||
slugs = set(s for s in slugs if s)
|
||||
|
||||
if not slugs:
|
||||
return {}
|
||||
|
||||
banners_available = {fn.split(".")[0] for fn in os.listdir(settings.BANNER_PATH)}
|
||||
icons_available = {
|
||||
fn.split(".")[0].replace("lutris_", "")
|
||||
|
@ -190,18 +199,18 @@ def sync_media() -> dict:
|
|||
}
|
||||
covers_available = {fn.split(".")[0] for fn in os.listdir(settings.COVERART_PATH)}
|
||||
complete_games = banners_available.intersection(icons_available).intersection(covers_available)
|
||||
all_slugs = {game["slug"] for game in get_games()}
|
||||
slugs = all_slugs - complete_games
|
||||
if not slugs:
|
||||
|
||||
slugs_to_download = slugs - complete_games
|
||||
if not slugs_to_download:
|
||||
return {}
|
||||
games = get_api_games(list(slugs))
|
||||
games = get_api_games(list(slugs_to_download))
|
||||
|
||||
alias_map = {}
|
||||
api_slugs = set()
|
||||
for game in games:
|
||||
api_slugs.add(game["slug"])
|
||||
for alias in game["aliases"]:
|
||||
if alias["slug"] in slugs:
|
||||
if alias["slug"] in slugs_to_download:
|
||||
alias_map[game["slug"]] = alias["slug"]
|
||||
alias_slugs = set(alias_map.values())
|
||||
used_alias_slugs = alias_slugs - api_slugs
|
||||
|
|
|
@ -18,6 +18,7 @@ from lutris.database.services import ServiceGameCollection
|
|||
from lutris.game import Game
|
||||
from lutris.installer import get_installers
|
||||
from lutris.services.base import OnlineService
|
||||
from lutris.services.lutris import sync_media
|
||||
from lutris.services.service_game import ServiceGame
|
||||
from lutris.services.service_media import ServiceMedia
|
||||
from lutris.util.log import logger
|
||||
|
@ -304,11 +305,13 @@ class OriginService(OnlineService):
|
|||
logger.error("Invalid install of Origin at %s", origin_prefix)
|
||||
return
|
||||
origin_launcher = OriginLauncher(origin_prefix)
|
||||
installed_games = 0
|
||||
installed_slugs = []
|
||||
for manifest in origin_launcher.iter_manifests():
|
||||
self.install_from_origin(origin_game, manifest)
|
||||
installed_games += 1
|
||||
logger.debug("Installed %s Origin games", installed_games)
|
||||
slug = self.install_from_origin(origin_game, manifest)
|
||||
if slug:
|
||||
installed_slugs.append(slug)
|
||||
sync_media(installed_slugs)
|
||||
logger.debug("Installed %s Origin games", len(installed_slugs))
|
||||
|
||||
def install_from_origin(self, origin_game, manifest):
|
||||
if "id" not in manifest:
|
||||
|
@ -326,10 +329,11 @@ class OriginService(OnlineService):
|
|||
game_config = LutrisConfig(game_config_id=origin_game["configpath"]).game_level
|
||||
game_config["game"]["args"] = get_launch_arguments(manifest["id"])
|
||||
configpath = write_game_config(lutris_game_id, game_config)
|
||||
game_id = add_game(
|
||||
slug = self.get_installed_slug(service_game)
|
||||
add_game(
|
||||
name=service_game["name"],
|
||||
runner=origin_game["runner"],
|
||||
slug=slugify(service_game["name"]),
|
||||
slug=slug,
|
||||
directory=origin_game["directory"],
|
||||
installed=1,
|
||||
installer_slug=lutris_game_id,
|
||||
|
@ -337,7 +341,7 @@ class OriginService(OnlineService):
|
|||
service=self.id,
|
||||
service_id=offer_id,
|
||||
)
|
||||
return game_id
|
||||
return slug
|
||||
|
||||
def generate_installer(self, db_game, origin_db_game):
|
||||
origin_game = Game(origin_db_game["id"])
|
||||
|
|
|
@ -13,6 +13,7 @@ from lutris.database.services import ServiceGameCollection
|
|||
from lutris.game import Game
|
||||
from lutris.installer.installer_file import InstallerFile
|
||||
from lutris.services.base import BaseService
|
||||
from lutris.services.lutris import sync_media
|
||||
from lutris.services.service_game import ServiceGame
|
||||
from lutris.services.service_media import ServiceMedia
|
||||
from lutris.util.log import logger
|
||||
|
@ -132,10 +133,11 @@ class SteamService(BaseService):
|
|||
game_config = LutrisConfig().game_level
|
||||
game_config["game"]["appid"] = appid
|
||||
configpath = write_game_config(lutris_game_id, game_config)
|
||||
game_id = add_game(
|
||||
slug = self.get_installed_slug(service_game)
|
||||
add_game(
|
||||
name=service_game["name"],
|
||||
runner="steam",
|
||||
slug=slugify(service_game["name"]),
|
||||
slug=slug,
|
||||
installed=1,
|
||||
installer_slug=lutris_game_id,
|
||||
configpath=configpath,
|
||||
|
@ -143,7 +145,7 @@ class SteamService(BaseService):
|
|||
service=self.id,
|
||||
service_id=appid,
|
||||
)
|
||||
return game_id
|
||||
return slug
|
||||
|
||||
@property
|
||||
def steamapps_paths(self):
|
||||
|
@ -152,6 +154,7 @@ class SteamService(BaseService):
|
|||
def add_installed_games(self):
|
||||
"""Syncs installed Steam games with Lutris"""
|
||||
stats = {"installed": 0, "removed": 0, "deduped": 0, "paths": []}
|
||||
installed_slugs = []
|
||||
installed_appids = []
|
||||
for steamapps_path in self.steamapps_paths:
|
||||
for appmanifest_file in get_appmanifests(steamapps_path):
|
||||
|
@ -160,7 +163,9 @@ class SteamService(BaseService):
|
|||
app_manifest_path = os.path.join(steamapps_path, appmanifest_file)
|
||||
app_manifest = AppManifest(app_manifest_path)
|
||||
installed_appids.append(app_manifest.steamid)
|
||||
self.install_from_steam(app_manifest)
|
||||
slug = self.install_from_steam(app_manifest)
|
||||
if slug:
|
||||
installed_slugs.append(slug)
|
||||
stats["installed"] += 1
|
||||
if stats["paths"]:
|
||||
logger.debug("%s Steam games detected and installed", stats["installed"])
|
||||
|
@ -196,6 +201,7 @@ class SteamService(BaseService):
|
|||
steam_game.remove(no_signal=True)
|
||||
steam_game.delete(no_signal=True)
|
||||
stats["deduped"] += 1
|
||||
sync_media(installed_slugs)
|
||||
logger.debug("%s Steam games deduplicated", stats["deduped"])
|
||||
|
||||
def generate_installer(self, db_game):
|
||||
|
|
|
@ -14,6 +14,7 @@ from lutris.database.services import ServiceGameCollection
|
|||
from lutris.game import Game
|
||||
from lutris.installer import get_installers
|
||||
from lutris.services.base import OnlineService
|
||||
from lutris.services.lutris import sync_media
|
||||
from lutris.services.service_game import ServiceGame
|
||||
from lutris.services.service_media import ServiceMedia
|
||||
from lutris.util.log import logger
|
||||
|
@ -191,12 +192,13 @@ class UbisoftConnectService(OnlineService):
|
|||
launch_id = details.get("launchId") or details.get("installId") or details.get("spaceId")
|
||||
game_config["game"]["args"] = f"uplay://launch/{launch_id}"
|
||||
configpath = write_game_config(lutris_game_id, game_config)
|
||||
slug = self.get_installed_slug(game)
|
||||
if existing_game:
|
||||
update_existing(
|
||||
id=existing_game["id"],
|
||||
name=game["name"],
|
||||
runner=self.runner,
|
||||
slug=slugify(game["name"]),
|
||||
slug=slug,
|
||||
directory=ubisoft_connect["directory"],
|
||||
installed=1,
|
||||
installer_slug=lutris_game_id,
|
||||
|
@ -205,10 +207,10 @@ class UbisoftConnectService(OnlineService):
|
|||
service_id=game["appid"],
|
||||
)
|
||||
return existing_game["id"]
|
||||
game_id = add_game(
|
||||
add_game(
|
||||
name=game["name"],
|
||||
runner=self.runner,
|
||||
slug=slugify(game["name"]),
|
||||
slug=slug,
|
||||
directory=ubisoft_connect["directory"],
|
||||
installed=1,
|
||||
installer_slug=lutris_game_id,
|
||||
|
@ -216,7 +218,7 @@ class UbisoftConnectService(OnlineService):
|
|||
service=self.id,
|
||||
service_id=game["appid"],
|
||||
)
|
||||
return game_id
|
||||
return slug
|
||||
|
||||
def add_installed_games(self):
|
||||
ubisoft_connect = get_game_by_field(self.client_installer, "slug")
|
||||
|
@ -225,12 +227,16 @@ class UbisoftConnectService(OnlineService):
|
|||
return
|
||||
prefix_path = ubisoft_connect["directory"].split("drive_c")[0]
|
||||
prefix = WinePrefixManager(prefix_path)
|
||||
installed_slugs = []
|
||||
for game in ServiceGameCollection.get_for_service(self.id):
|
||||
details = json.loads(game["details"])
|
||||
install_path = get_ubisoft_registry(prefix, details.get("registryPath"))
|
||||
exe = get_ubisoft_registry(prefix, details.get("exe"))
|
||||
if install_path and exe:
|
||||
self.install_from_ubisoft(ubisoft_connect, game)
|
||||
slug = self.install_from_ubisoft(ubisoft_connect, game)
|
||||
if slug:
|
||||
installed_slugs.append(slug)
|
||||
sync_media(installed_slugs)
|
||||
|
||||
def generate_installer(self, db_game, ubi_db_game):
|
||||
ubisoft_connect = Game(ubi_db_game["id"])
|
||||
|
|
Loading…
Reference in a new issue