Add install locations, upload picker, update flags, move, and launch hooks (v0.1.24)
This commit is contained in:
parent
a024907696
commit
ff2ae68d9e
3 changed files with 252 additions and 29 deletions
|
|
@ -515,10 +515,72 @@ func _ensure_install_location() -> String:
|
|||
return _install_location_id
|
||||
|
||||
|
||||
## Returns every configured install location, each as:
|
||||
## {"id": "...", "path": "...", "sizeInfo": {"installedSize", "freeSize", "totalSize"}}
|
||||
func get_install_locations() -> Array:
|
||||
return await thread_group.exec(_get_install_locations)
|
||||
|
||||
|
||||
func _get_install_locations() -> Array:
|
||||
var res := await _rpc_call("Install.Locations.List", {})
|
||||
if "error" in res:
|
||||
logger.warn("Install.Locations.List failed: " + str(res["error"]))
|
||||
return []
|
||||
return res.get("installLocations", [])
|
||||
|
||||
|
||||
## Returns the uploads butlerd considers compatible with this machine for the
|
||||
## given game. Each entry is a full Upload dict (id, filename, displayName,
|
||||
## size, type, platforms, ...).
|
||||
func get_compatible_uploads(game_id: int) -> Array:
|
||||
return await thread_group.exec(_get_compatible_uploads.bind(game_id))
|
||||
|
||||
|
||||
func _get_compatible_uploads(game_id: int) -> Array:
|
||||
var params := {"gameId": game_id, "compatible": true}
|
||||
var res := await _rpc_call("Fetch.GameUploads", params)
|
||||
if "error" in res:
|
||||
logger.warn("Fetch.GameUploads failed for game " + str(game_id) + ": " + str(res["error"]))
|
||||
return []
|
||||
# Uploads are cached by butlerd just like owned keys, so retry fresh when
|
||||
# the cached result is stale, otherwise a cold database would report every
|
||||
# game as having no compatible upload.
|
||||
if res.get("stale", false):
|
||||
logger.info("Uploads are stale. Refreshing from itch.io.")
|
||||
params["fresh"] = true
|
||||
res = await _rpc_call("Fetch.GameUploads", params)
|
||||
if "error" in res:
|
||||
logger.warn("Fetch.GameUploads (fresh) failed for game " + str(game_id) + ": " + str(res["error"]))
|
||||
return []
|
||||
return res.get("uploads", [])
|
||||
|
||||
|
||||
## Looks for updates to installed caves. When cave_ids is empty, butlerd checks
|
||||
## every cave and respects per-cave snooze (matching the official itch.io app).
|
||||
## Returns the list of GameUpdate dicts (each with a `caveId`).
|
||||
func check_updates(cave_ids: Array = []) -> Array:
|
||||
return await thread_group.exec(_check_updates.bind(cave_ids))
|
||||
|
||||
|
||||
func _check_updates(cave_ids: Array) -> Array:
|
||||
var params := {}
|
||||
if not cave_ids.is_empty():
|
||||
params["caveIds"] = cave_ids
|
||||
var res := await _rpc_call("CheckUpdate", params)
|
||||
if "error" in res:
|
||||
logger.warn("CheckUpdate failed: " + str(res["error"]))
|
||||
return []
|
||||
return res.get("updates", [])
|
||||
|
||||
|
||||
## Installs (or updates, if a caveId is given) the given game.
|
||||
## `options` may carry:
|
||||
## - "install_location_id": the InstallLocation id to install into (fresh installs)
|
||||
## - "upload": the upload filename/displayName chosen by the user (defaults to
|
||||
## butlerd's first compatible upload otherwise)
|
||||
## Returns true when the operation finished without errors.
|
||||
func install(game: Dictionary, cave_id: String = "") -> bool:
|
||||
return await thread_group.exec(_install.bind(game, cave_id))
|
||||
func install(game: Dictionary, cave_id: String = "", options: Dictionary = {}) -> bool:
|
||||
return await thread_group.exec(_install.bind(game, cave_id, options))
|
||||
|
||||
|
||||
## Reconciles an install/update request against what butlerd actually has
|
||||
|
|
@ -560,36 +622,30 @@ func _resolve_cave(game: Dictionary, cave_id: String) -> String:
|
|||
return ""
|
||||
|
||||
|
||||
func _install(game: Dictionary, cave_id: String) -> bool:
|
||||
func _install(game: Dictionary, cave_id: String, options: Dictionary) -> bool:
|
||||
var game_id: int = game.get("id", 0)
|
||||
cave_id = await _resolve_cave(game, cave_id)
|
||||
var reason := "update" if cave_id != "" else "install"
|
||||
|
||||
var uploads_params := {"gameId": game_id, "compatible": true}
|
||||
var uploads_res := await _rpc_call("Fetch.GameUploads", uploads_params)
|
||||
if "error" in uploads_res:
|
||||
logger.warn("Fetch.GameUploads failed: " + str(uploads_res["error"]))
|
||||
emit_signal.call_deferred("app_installed", cave_id, false)
|
||||
return false
|
||||
# Uploads are cached by butlerd just like owned keys, so retry fresh when
|
||||
# the cached result is stale, otherwise a cold database would report every
|
||||
# game as having no compatible upload.
|
||||
if uploads_res.get("stale", false):
|
||||
logger.info("Uploads are stale. Refreshing from itch.io.")
|
||||
uploads_params["fresh"] = true
|
||||
uploads_res = await _rpc_call("Fetch.GameUploads", uploads_params)
|
||||
if "error" in uploads_res:
|
||||
logger.warn("Fetch.GameUploads (fresh) failed: " + str(uploads_res["error"]))
|
||||
emit_signal.call_deferred("app_installed", cave_id, false)
|
||||
return false
|
||||
var uploads: Array = uploads_res.get("uploads", [])
|
||||
var uploads := await _get_compatible_uploads(game_id)
|
||||
if uploads.is_empty():
|
||||
logger.error("No compatible uploads found for game id " + str(game_id))
|
||||
emit_signal.call_deferred("app_installed", cave_id, false)
|
||||
return false
|
||||
var upload: Dictionary = uploads[0]
|
||||
# Respect the upload the user picked in the install-options dialog, when
|
||||
# one was offered (the game has more than one compatible upload).
|
||||
var chosen_upload: Variant = options.get("upload", null)
|
||||
if chosen_upload != null:
|
||||
var picked := _pick_upload(uploads, str(chosen_upload))
|
||||
if picked.is_empty():
|
||||
logger.warn("Selected upload '" + str(chosen_upload) + "' not found. Using default upload.")
|
||||
else:
|
||||
upload = picked
|
||||
|
||||
var location_id := await _ensure_install_location()
|
||||
var location_id: String = options.get("install_location_id", "") as String
|
||||
if location_id == "":
|
||||
location_id = await _ensure_install_location()
|
||||
|
||||
var queue_params := {
|
||||
"game": game,
|
||||
|
|
@ -637,6 +693,16 @@ func _install(game: Dictionary, cave_id: String) -> bool:
|
|||
return success
|
||||
|
||||
|
||||
## Returns the upload whose displayName or filename matches the user's pick
|
||||
## from the install-options dropdown, or {} when nothing matches.
|
||||
func _pick_upload(uploads: Array, chosen: String) -> Dictionary:
|
||||
for u in uploads:
|
||||
var upload: Dictionary = u
|
||||
if upload.get("displayName", "") == chosen or upload.get("filename", "") == chosen:
|
||||
return upload
|
||||
return {}
|
||||
|
||||
|
||||
## Uninstalls the given cave. Returns true on success.
|
||||
func uninstall(cave_id: String) -> bool:
|
||||
return await thread_group.exec(_uninstall.bind(cave_id))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue