Skip to main content

A robust, type-safe Python wrapper for the Ludusavi game backup tool.

Project description

pyludusavi

A robust, type-safe Python wrapper for the Ludusavi CLI.

Features

  • Broad CLI Coverage: Supports the core Ludusavi subcommands and commonly used flags.
  • Linux-First: Native support for both local binaries and Flatpak.
  • Type-Safe: Comprehensive TypedDict models for all JSON outputs (Python 3.12+).
  • Dual-Mode Execution: Transparently handles binary vs. Flatpak command prefixing.
  • TDD-Backed: High-quality implementation with an extensive regression suite.

Setup

For local development, use the project wrapper so virtual environments and tool caches stay outside Dropbox:

source .envrc
./run.sh uv sync

Run validation through the same wrapper:

./run.sh uv run ruff check .
./run.sh uv run ty check src/
./run.sh uv run pytest

Installation

Use uv when adding the package to another Python project:

uv add pyludusavi

Quick Start

Basic Initialization

The wrapper automatically discovers Ludusavi via PATH or Flatpak.

from pyludusavi import Ludusavi

lud = Ludusavi()
print(f"Ludusavi version: {lud.version()}")

Backing Up Games

Perform a preview scan or a full backup.

# Preview a backup for a specific game
result = lud.backup(games=["The Witcher 3"], preview=True)

# Access typed data
for name, game in result.data["games"].items():
    print(f"Game: {name}, Status: {game['change']}")

Searching for Games

Use Steam IDs or fuzzy matching to find games in the manifest.

# Find by Steam ID
result = lud.find(steam_id="292030")

# Use fuzzy matching
result = lud.find(games=["Witcher"], fuzzy=True)

Advanced Usage

Flatpak Support

If you have Ludusavi installed via Flatpak, pyludusavi detects it automatically. You can also specify a custom binary path or Flatpak ID:

lud = Ludusavi(explicit_path="/usr/bin/ludusavi")
lud = Ludusavi(flatpak_id="com.github.mtkennerly.ludusavi")

Custom Config Directory

lud = Ludusavi(config_dir="/home/user/my-ludusavi-config")

Custom Environment

Pass environment overrides when Ludusavi needs a different PATH, HOME, or launcher-specific variable. Overrides are merged onto the current process environment and used for both discovery and command execution.

lud = Ludusavi(env={"PATH": "/opt/ludusavi/bin:/usr/bin", "HOME": "/home/deck"})

Bulk API

For performance-critical bulk operations, use the native api subcommand:

payload = {
    "requests": [
        {"kind": "backup", "games": ["Game 1"]},
        {"kind": "backup", "games": ["Game 2"]}
    ]
}
lud.bulk_api(payload)

Cloud Sync

Use the upload/download helpers with the same common options exposed by the CLI.

lud.cloud_upload(games=["The Witcher 3"], local="/backups", cloud="/cloud", preview=True)
lud.cloud_download(games=["The Witcher 3"], force=True)

Wrap Game Launch

Ludusavi requires either a direct game name or launcher inference when wrapping a command.

lud.wrap(["./game.exe", "--windowed"], name="The Witcher 3")
lud.wrap(["steam", "-applaunch", "292030"], infer="steam", force=True)

Game Aliases

add_game_alias() maps a custom name to an existing manifest title by updating Ludusavi's customGames configuration using only the Python standard library. It is idempotent (no write occurs if the alias already exists unchanged), updates the target in place if the same name already points at a different alias, and writes atomically (via a temporary file and rename) so an interrupted write cannot corrupt config.yaml. The config is written as JSON, which Ludusavi reads as YAML; this does not preserve existing comments or formatting. Use get_game_alias() to read back the alias for a custom name (returns None if not found).

lud.add_game_alias("My Game", "The Witcher 3")
lud.get_game_alias("My Game")  # -> "The Witcher 3"

Timeouts

Quick, bounded commands (version, find, config_*, backups_list, schema, complete) default to a 30-second timeout. Long-running operations — backup, restore, wrap, cloud_upload, cloud_download, manifest_update, and bulk_api — default to no timeout (timeout=None) so a long game session or large transfer is not killed. Every one of these accepts an explicit timeout=<seconds> override. A timeout raises LudusaviTimeoutError.

Note: The automatic background probe that verifies the Ludusavi executable during discovery has a fixed, non-configurable 15-second timeout. Timeout expiration rejects the candidate (falling back or raising LudusaviNotFoundError) rather than surfacing a timeout error.

# Wrap a game launch with no time limit (default)
lud.wrap(["./game.exe"], name="The Witcher 3")

# Cap a cloud upload at 5 minutes
lud.cloud_upload(games=["The Witcher 3"], timeout=300)

Reading Logs

Inspect Ludusavi's log directory and current log file:

lud.log_dir()   # -> "/home/user/.config/ludusavi"
lud.log_show()  # -> contents of ludusavi_rCURRENT.log, or "" if it does not exist

Error Handling

  • LudusaviNotFoundError: Raised if the executable or Flatpak isn't found.
  • LudusaviExecutionError: Raised if the process exits with a non-zero code.
  • LudusaviContractError: Raised if the CLI output is malformed or non-JSON when expected.
  • LudusaviTimeoutError: Raised if the process exceeds its timeout. Subclass of LudusaviError, so existing except LudusaviError handlers still catch it.

Dependency Requirements

  • Python 3.12+
  • uv
  • Ludusavi v0.31.0+
  • pytest, pytest-cov, ruff, and ty for local development

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyludusavi-0.2.6.tar.gz (13.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyludusavi-0.2.6-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

Details for the file pyludusavi-0.2.6.tar.gz.

File metadata

  • Download URL: pyludusavi-0.2.6.tar.gz
  • Upload date:
  • Size: 13.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyludusavi-0.2.6.tar.gz
Algorithm Hash digest
SHA256 93c51e1194d4c2f4e72f55a29c59aebb3a11849dbdc1e4a5366452fb638f1565
MD5 fd80f4489b01dba4250fdccb8ccf3ed9
BLAKE2b-256 d67f9a1763bb2761af6e18471ac5ca567c2b8b76c464d94bf8a43414603560de

See more details on using hashes here.

File details

Details for the file pyludusavi-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: pyludusavi-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 15.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyludusavi-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 4e6e425c4ef43bf8810b5fcc3912a1c61291d329dd979b71a946064c1048e16b
MD5 c08506625437a5cc6a734f10e6f92a72
BLAKE2b-256 49fab3a93c9f52711297a4d455fdc457101c04905f4a6b4771414d333978553b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page