feat: Playnite library sync plugin + exporter
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>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
using System.Collections.Generic;
|
||||
using Playnite.SDK.Data;
|
||||
|
||||
namespace PunktfunkSync
|
||||
{
|
||||
// The on-disk contract with the Punktfunk `playnite` plugin. Keep these property names in lockstep
|
||||
// with `src/export-schema.ts` (ExportedGame / LibraryExport). Serialized via the Playnite SDK's
|
||||
// `Serialization.ToJson`, so `[SerializationPropertyName]` pins the JSON keys regardless of the
|
||||
// serializer's default casing.
|
||||
|
||||
public class ExportedGame
|
||||
{
|
||||
// Playnite's stable game Guid ("d"-format, lowercase with dashes) — the reconcile external_id.
|
||||
[SerializationPropertyName("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[SerializationPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[SerializationPropertyName("installed")]
|
||||
public bool Installed { get; set; }
|
||||
|
||||
[SerializationPropertyName("hidden")]
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
// Library source name ("Steam", "GOG", "Epic", emulator name…) or null.
|
||||
[SerializationPropertyName("source")]
|
||||
public string Source { get; set; }
|
||||
|
||||
[SerializationPropertyName("platforms")]
|
||||
public List<string> Platforms { get; set; }
|
||||
|
||||
// ABSOLUTE paths on the host box (already resolved from Playnite's database-relative form), or
|
||||
// null. A remote (http) art URL is passed through verbatim.
|
||||
[SerializationPropertyName("cover")]
|
||||
public string Cover { get; set; }
|
||||
|
||||
[SerializationPropertyName("background")]
|
||||
public string Background { get; set; }
|
||||
|
||||
[SerializationPropertyName("icon")]
|
||||
public string Icon { get; set; }
|
||||
}
|
||||
|
||||
public class PlayniteInfo
|
||||
{
|
||||
[SerializationPropertyName("version")]
|
||||
public string Version { get; set; }
|
||||
|
||||
[SerializationPropertyName("mode")]
|
||||
public string Mode { get; set; }
|
||||
}
|
||||
|
||||
public class LibraryExport
|
||||
{
|
||||
[SerializationPropertyName("schema")]
|
||||
public int Schema { get; set; }
|
||||
|
||||
[SerializationPropertyName("generatedAt")]
|
||||
public string GeneratedAt { get; set; }
|
||||
|
||||
[SerializationPropertyName("playnite")]
|
||||
public PlayniteInfo Playnite { get; set; }
|
||||
|
||||
[SerializationPropertyName("games")]
|
||||
public List<ExportedGame> Games { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user