# @punktfunk/plugin-playnite Sync your **[Playnite](https://playnite.link)** library into the Punktfunk host game library. Every store and emulator Playnite manages — Steam, GOG, Epic, Xbox, itch, RetroArch, standalone emulators, manually-added games — shows up in Punktfunk's grid on every client, and launching a title hands it straight back to Playnite, which performs the real launch. It has two halves, both on the **same Windows box** as the Punktfunk host: ``` ┌─────────────────────────────┐ ┌──────────────────────────────────────────┐ │ Playnite │ │ Punktfunk host (this plugin, in the │ │ └ Punktfunk Sync extension │ JSON │ scripting runner) │ │ writes │ ─────▶ │ reads punktfunk-library.json, embeds art, │ │ punktfunk-library.json │ │ PUT /library/provider/playnite │ └─────────────────────────────┘ └──────────────────────────────────────────┘ ``` - **The Playnite exporter** (`exporter/`, C#) is a tiny [GenericPlugin](https://api.playnite.link/docs/tutorials/extensions/genericPlugins.html) that writes your library to a JSON file whenever it changes. Playnite locks its library database while running, so reading it from *inside* Playnite is the only robust way — this is that adapter, and nothing more. - **This plugin** (TypeScript, on [`@punktfunk/host`](https://git.unom.io/unom/punktfunk/src/branch/main/sdk)) watches that file and reconciles it into the host library as the `playnite` provider, with a console-hosted web UI. Zero-config auth via the runner. ## Install Two one-time steps. ### 1 · This plugin (on the host) ```powershell cd "$(punktfunk config-dir)\plugins" # e.g. C:\ProgramData\punktfunk\plugins # bunfig.toml points the @punktfunk scope at the registry (once) bun add @punktfunk/plugin-playnite Enable-ScheduledTask PunktfunkScripting # enable the scripting runner if it isn't already ``` Open the Punktfunk console — a **Playnite** entry appears in the nav. (Headless box without the console? The engine is fully functional from a `config.json` alone — see [Headless](#headless--cli).) ### 2 · The Punktfunk Sync extension (in Playnite) Download **`punktfunk-sync.pext`** from the [latest CI build](https://git.unom.io/unom/punktfunk-plugin-playnite/actions) (the `exporter` job's artifact) and **double-click it** — Playnite installs it like any add-on. Restart Playnite once. That's it. The console's **Playnite** page shows "Exporter connected" and your games sync within seconds of any library change. > The console page can also **deploy the exporter for you** if you'd rather not handle the `.pext` — > it drops the extension into `%APPDATA%\Playnite\Extensions\` and asks you to restart Playnite. > (Best-effort: it needs read/write access to the interactive user's `%APPDATA%`; the `.pext` is the > reliable fallback.) ## Launching Each title syncs with a launch command of `start "" "playnite://playnite/start/"`. The host runs that in the interactive Windows session, so **Playnite** owns the actual launch — the correct store client or emulator, exactly as if you'd hit Play in Playnite. No per-store launch mapping to maintain. ## Box art Playnite stores cover art as **local files** on the host. Rather than shipping image bytes in the reconcile (which doesn't scale — a large library blows past the host's request-body limit), the plugin sends the local file **path** and the host serves the cover through its art proxy (`/library/art/…`, exactly like Steam art). The payload stays tiny at any library size. | `art.mode` | Behaviour | |--------------|-----------------------------------------------------------------| | `host` | **Default.** Send the cover's local path; the host serves the bytes. Scales to thousands of titles. Requires a host with the provider-art proxy. | | `dataurl` | Inline the cover as a size-capped `data:` URL (`maxBytes` caps one image). Self-contained, but for **small** libraries only — a big one exceeds the host's body limit. | | `off` | Sync titles with no art (lightest payload). | `art.includeBackground` additionally serves the Playnite background as `hero` art (off by default). ## Filters | Key | Default | Meaning | |-------------------------|---------|----------------------------------------------------------------| | `filter.installedOnly` | `true` | Only sync games Playnite marks installed. | | `filter.includeHidden` | `false` | Include games flagged Hidden in Playnite. | | `filter.sources` | `[]` | If non-empty, keep only these sources (`"Steam"`, `"GOG"`…). Case-insensitive. | | `filter.excludeSources` | `[]` | Drop these sources. | Edit them in the console's **Playnite** page, or in `config.json` directly. ## Headless / CLI The plugin owns `/playnite/config.json` (see [`config.example.json`](./config.example.json)). It's fully functional from that file alone. A debug CLI mirrors the engine: ```sh bunx punktfunk-plugin-playnite where # where the exporter output was found (+ probed paths) bunx punktfunk-plugin-playnite preview # the desired library — no host write bunx punktfunk-plugin-playnite sync # reconcile into the live library bunx punktfunk-plugin-playnite uninstall # remove every entry this plugin owns ``` For a host without the console, set `ui.standalone: true` and a password (`bunx punktfunk-plugin-playnite set-password `); the UI then serves on `ui.port` (default 47994). ## Requirements - A **Windows** Punktfunk host running the scripting runner (`punktfunk-scripting`). - **Playnite** on the same box, with the Punktfunk Sync extension installed. - The mgmt-API provider reconcile is loopback + token; the runner supplies both automatically. ## Build from source ```sh bun install bun run build:all # backend (dist/) + SPA (dist/ui/) bun test ``` The exporter (needs the .NET SDK; builds net462 on any OS via reference assemblies): ```sh dotnet build exporter/PunktfunkSync.csproj -c Release # package the .pext: ( cd exporter/bin/Release && zip -j ../../../punktfunk-sync.pext extension.yaml PunktfunkSync.dll ) ``` ## License MIT OR Apache-2.0.