6694be9848
A two-part Punktfunk plugin that syncs the Playnite library into the host
game library as the `playnite` provider:
- exporter/ — a minimal C# Playnite GenericPlugin ("Punktfunk Sync") that
writes punktfunk-library.json on every library change. Reading Playnite's
live-locked LiteDB from outside isn't robust, so this adapter runs inside
Playnite. Builds net462 via reference assemblies; packaged as a .pext.
- src/ — the TS plugin (on @punktfunk/host, rom-manager shape): watches the
export, embeds covers as data URLs, reconciles via
PUT /library/provider/playnite, with a console-hosted web UI + standalone
fallback + CLI. Launch = playnite://playnite/start/<id>, run by the host
in the interactive session so Playnite performs the real launch.
Verified locally: backend tsc + 23 tests + biome clean, SPA build, C#
exporter build + .pext pack.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
129 lines
6.5 KiB
Markdown
129 lines
6.5 KiB
Markdown
# @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/<gameId>"`. 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**, and the host only proxies Steam art — so this plugin
|
|
inlines each cover as a size-capped `data:` URL that clients render directly (no network fetch).
|
|
|
|
| `art.mode` | Behaviour |
|
|
|--------------|-----------------------------------------------------------------|
|
|
| `dataurl` | Embed the cover (default). `maxBytes` caps a single image; oversized covers are skipped. |
|
|
| `off` | Sync titles with no art (lightest payload). |
|
|
|
|
`art.includeBackground` additionally embeds the Playnite background as `hero` art (large — off by
|
|
default). For a very large library, inlined covers make a heavy library payload — the console warns
|
|
above ~3000 titles; narrow it with a source filter or set `art.mode` to `off`.
|
|
|
|
## 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 `<config_dir>/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 <pw>`); 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.
|