@lookloot/capture-sdk
v1.0.6
Published
Public LookLoot capture SDK for partner desktop apps.
Maintainers
Readme
LookLoot Capture SDK
Add LookLoot capture to a partner desktop app with a small app-side client and a server-side token route.
Install
pnpm add @lookloot/capture-sdkThe package is ESM-only, targets Node.js 22 or newer, and belongs in the Electron main-process package. The host app must provide Electron. The SDK package bundles Windows input/audio helpers and the FFmpeg fallback dependency.
Partners install only @lookloot/capture-sdk. The private runtime and shared capture contracts are bundled into the published SDK build, so partners do not add @lookloot/capture-runtime or @lookloot/capture-contracts to their app.
Windows game capture normally requires a bundled OBS Studio portable runtime. Do not depend on a user-installed OBS copy. For packaged Electron builds, copy the portable OBS root to resources/obs so resources/obs/bin/64bit/obs64.exe exists. For local development or custom packagers, pass obsPath to the SDK or set LOOKLOOT_OBS_PATH. Overwolf Electron apps can choose captureEngine: "overwolf-recorder" and package Overwolf's Recorder module instead of OBS.
Prepare that runtime during your app build:
npx lookloot-capture-prepare-obs --out vendor/obsThe prepare command is idempotent. Once vendor/obs/bin/64bit/obs64.exe exists for the requested OBS version, it exits without re-downloading or re-extracting OBS. Re-run with --force only when you want to refresh the output directory. The download cache defaults to the OS user cache directory; pass --cache or set LOOKLOOT_OBS_CACHE_DIR when CI needs a managed cache path.
1. Mint device tokens on your backend
Keep your LookLoot partner key on your server. Your app should call your own backend, and your backend should exchange the registered source app ID plus either a signed-in user ID or an anonymous SDK install ID for a short-lived LookLoot device token.
// Example Express-style route on your backend.
app.post("/api/lookloot/device-token", optionalUser, async (req, res) => {
const externalUserId = req.user?.id ? `user:${req.user.id}` : undefined;
const anonymousId = externalUserId ? undefined : req.body?.anonymousId;
const upstream = await fetch("https://www.lookloot.gg/api/partner/auth/token", {
method: "POST",
headers: {
authorization: `Bearer ${process.env.LOOKLOOT_PARTNER_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify({
externalUserId,
anonymousId,
appId: req.body?.appId,
appVersion: req.body?.appVersion,
deviceName: req.body?.deviceName,
}),
});
if (!upstream.ok) {
const detail = await upstream.text().catch(() => "");
console.error("[lookloot] token upstream failed", upstream.status, detail);
res.status(502).json({ error: "Could not create LookLoot device token" });
return;
}
const data = await upstream.json();
res.json({
token: data.token,
deviceId: data.deviceId,
partnerEndUserId: data.partnerEndUserId,
externalUserId: data.externalUserId,
anonymous: data.anonymous,
app: data.app,
});
});Only token is required by the SDK. The other response fields are useful for your own logs and device-management UI.
appId must be an active LookLoot-registered app slug for your partner account; it is not freeform client metadata. externalUserId is optional when the user is not signed in. In that case, forward the SDK-provided anonymousId; LookLoot stores a stable hash of it instead of a raw user identity.
The token endpoint accepts appId plus exactly one of externalUserId or anonymousId. appVersion and deviceName are optional. Additional request fields are ignored.
2. Initialize in your app
import { app } from "electron";
import { join } from "node:path";
import { LookLootCapture } from "@lookloot/capture-sdk";
const capture = new LookLootCapture({
appId: "your-registered-app-slug",
appVersion: app.getVersion(),
dataDir: join(app.getPath("userData"), "lookloot"),
deviceName: app.getName(),
obsPath: process.env.LOOKLOOT_OBS_PATH,
captureEngine: "auto",
getDeviceToken: async ({ appId, appVersion, deviceName, anonymousId }) => {
const res = await fetch("https://your-api.example.com/api/lookloot/device-token", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ appId, appVersion, deviceName, anonymousId }),
});
if (!res.ok) throw new Error("LookLoot token request failed");
const data = await res.json();
return data.token;
},
});
capture.on("state-change", (state) => {
// "idle" | "active"
});
capture.on("error", (error) => {
console.error("[lookloot]", error);
});
await capture.init();dataDir is optional. Passing your app's userData path is recommended so working files stay inside your app profile.
obsPath is optional in packaged Electron apps when OBS is bundled at resources/obs. It is recommended for local smoke tests and nonstandard packagers.
captureEngine is optional and defaults to "auto", which currently uses the bundled OBS runtime. Use "overwolf-recorder" only in Overwolf Electron builds that include "recorder" in overwolf.packages and pass app.overwolf.packages to the SDK.
Apps that already install a privileged helper can also pass elevatedCapturePipeName. This lets the SDK route protected/admin game capture through that helper without exposing OBS UI or falling back to desktop capture. Most partner apps should omit it unless they own that helper lifecycle.
If omitted, the SDK uses an OS-appropriate LookLoot folder:
| OS | Default |
| --- | --- |
| macOS | ~/Library/Application Support/LookLoot/Capture |
| Windows | %LOCALAPPDATA%\LookLoot\Capture |
| Linux | $XDG_STATE_HOME/lookloot/capture or ~/.local/state/lookloot/capture |
3. Package the Windows capture runtime
The SDK package is intentionally not bloated with the full OBS runtime. Your Windows installer must include the official portable OBS Studio runtime as an Electron resource:
{
"build": {
"extraResources": [
{
"from": "vendor/obs",
"to": "obs",
"filter": ["**/*"]
}
],
"asarUnpack": [
"**/node_modules/ffmpeg-static/**",
"**/node_modules/@lookloot/capture-sdk/dist/input-helper/**/*",
"**/node_modules/@lookloot/capture-sdk/dist/audio-helper/**/*"
]
}
}vendor/obs should be the portable OBS root that contains bin/64bit/obs64.exe, data, and obs-plugins. Package it only through extraResources; if your app files glob includes vendor, add an exclusion such as !vendor/** so vendor/obs is not packaged twice. Keep any custom OBS download cache outside packaged app files. After packaging, verify that your unpacked app contains resources/obs/bin/64bit/obs64.exe.
API
new LookLootCapture(options)
await capture.init()
await capture.start()
await capture.stop()
await capture.dispose()
capture.getState()
capture.getRuntimeDiagnostics()
await capture.runLocalVerification()
await capture.openLocalVerificationViewer()
capture.on("state-change", listener)
capture.on("error", listener)Most apps only need init() and the state-change event. After init() succeeds, the SDK watches for detected game processes and starts recording automatically as soon as one is detected. Recording stops when the game exits, when stop() is called, or when dispose() runs during app shutdown. The SDK is not limited to a LookLoot telemetry allowlist; it detects known game processes and common game-library install paths while filtering normal desktop/browser windows. The SDK installs an Electron before-quit cleanup handler by default so sudden app exits still get a chance to finalize the active session; call dispose() yourself if your app has a custom shutdown flow. init() is safe to call again to refresh a rotated device token. start() remains available for explicit retry/manual capture requests and returns Promise<{ ok: true } | { ok: false; reason: string }>; getRuntimeDiagnostics() lets installers and smoke tests confirm the selected capture engine, FFmpeg, and helper assets are present before capture starts.
For developer self-checks, runLocalVerification() records a short disk-only local capture without calling getDeviceToken or uploading anything. Open a game, keep it focused, press a few keys or click while it runs, then inspect result.video, result.input, and result.sessionDir. Call openLocalVerificationViewer(result) to open a bundled visual report with local video playback, game/status checks, and keyboard/mouse input highlights. This verifies local video and input capture before checking Deployed Telemetry dashboard ingestion.
Security Notes
- Do not ship
LOOKLOOT_PARTNER_KEYin desktop or browser code. - The public SDK only exposes coarse
"idle"/"active"state. - Published packages ship the obfuscated bundled runtime, type declarations, this README, recorder helper HTML, the OBS preparation CLI, and bundled Windows input/audio helpers. Source maps and source files are not published.
- OBS Studio is redistributed as a separate runtime in your desktop installer, not embedded into the npm package.
