@lookloot/capture-sdk-overwolf-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # LookLoot Capture SDK for Overwolf Native
2
+
3
+ Use this package from a classic Overwolf Native app background window. It mirrors the public `LookLootCapture` API from the Electron SDK, but records through Overwolf Native replay, game-event, and input-tracking APIs by default.
4
+
5
+ ```bash
6
+ pnpm add @lookloot/capture-sdk-overwolf-native
7
+ ```
8
+
9
+ ```ts
10
+ import { LookLootCapture } from "@lookloot/capture-sdk-overwolf-native";
11
+
12
+ const capture = new LookLootCapture({
13
+ appId: "your-registered-app-slug",
14
+ appVersion: "1.0.0",
15
+ getDeviceToken: async ({ appId, appVersion, deviceName, anonymousId }) => {
16
+ const res = await fetch("https://your-api.example.com/api/lookloot/device-token", {
17
+ method: "POST",
18
+ headers: { "content-type": "application/json" },
19
+ body: JSON.stringify({ appId, appVersion, deviceName, anonymousId }),
20
+ });
21
+ const data = await res.json();
22
+ return data.token;
23
+ },
24
+ uploadBridge: window.looklootOverwolfUploadBridge,
25
+ });
26
+
27
+ await capture.init();
28
+ ```
29
+
30
+ The upload bridge is required for production video uploads because Overwolf replay capture returns a local `media_path`. JSON artifacts upload directly with browser `fetch`.
31
+
32
+ ## Capture backend choice
33
+
34
+ The default `replay.captureBackend` is `"overwolf_recorder"`. Use it when you want Overwolf Recorder as the no-OBS capture engine:
35
+
36
+ ```ts
37
+ const capture = new LookLootCapture({
38
+ appId: "your-registered-app-slug",
39
+ getDeviceToken,
40
+ uploadBridge: window.looklootOverwolfUploadBridge,
41
+ upload: { videoBackend: "chunked_hls" },
42
+ });
43
+ ```
44
+
45
+ Apps that need user-selected window capture outside Overwolf Recorder support can opt into:
46
+
47
+ ```ts
48
+ const capture = new LookLootCapture({
49
+ appId: "your-registered-app-slug",
50
+ getDeviceToken,
51
+ replay: { captureBackend: "browser_display_media" },
52
+ upload: { videoBackend: "chunked_hls" },
53
+ });
54
+ ```
55
+
56
+ `browser_display_media` rejects monitor and browser-tab selections so it does not upload desktop capture. It uses playable WebM browser `Blob` chunks and does not require the file upload bridge for video.
57
+
58
+ ## LookLoot-style native integration
59
+
60
+ For apps that want the same integrated path used by LookLoot's Overwolf Native app, use the SDK facade instead of recreating the app glue yourself. It wires the native input adapter, native process game detection, direct game-window stream selection, and chunked upload defaults around `LookLootCapture`.
61
+
62
+ ```ts
63
+ import {
64
+ createLookLootOverwolfNativeIntegration,
65
+ } from "@lookloot/capture-sdk-overwolf-native";
66
+
67
+ const lookloot = createLookLootOverwolfNativeIntegration({
68
+ appId: "your-registered-app-slug",
69
+ getDeviceToken,
70
+ nativeProcess: {
71
+ extraObjectId: "lookloot-process-presence",
72
+ gameCatalog: [
73
+ {
74
+ gameId: "roblox",
75
+ displayName: "Roblox",
76
+ processNames: ["RobloxPlayerBeta.exe"],
77
+ },
78
+ ],
79
+ },
80
+ });
81
+
82
+ await lookloot.init();
83
+ await lookloot.start(); // detects the running game and requests the game window source
84
+ await lookloot.stop();
85
+ ```
86
+
87
+ The lower-level pieces are exported too: `createLookLootOverwolfNativeInputSource()`, `getLookLootOverwolfRunningGame()`, `detectLookLootOverwolfNativeProcessGames()`, `requestLookLootOverwolfGameWindowSource()`, and `resolveLookLootOverwolfNativeProcessPlugin()`.
88
+
89
+ ## Verify locally before upload
90
+
91
+ Use the native verification helpers from a debug menu or smoke-test command. They do not call `getDeviceToken`, create a LookLoot session, or upload anything.
92
+
93
+ ```ts
94
+ const diagnostics = capture.getRuntimeDiagnostics();
95
+ const result = await capture.runLocalVerification({ durationMs: 8000 });
96
+ await capture.openLocalVerificationViewer(result);
97
+ ```
98
+
99
+ Open a game, keep it focused, and press keys or click during the verification window. Passing local verification means `result.video.ok === true`, `result.video.replayId` is present, `result.video.mediaPath` or `result.video.mediaUrl` is present, and `result.input.eventCount > 0`.
100
+
101
+ After local verification passes, run a normal token-backed capture and verify remote ingestion with `GET https://www.lookloot.gg/api/partner/captures/verify`.
@@ -0,0 +1,456 @@
1
+ import { InputEvent } from '@lookloot/capture-contracts';
2
+
3
+ type LookLootDeviceTokenContext = {
4
+ appId: string;
5
+ appVersion?: string;
6
+ deviceName?: string;
7
+ anonymousId: string;
8
+ };
9
+ type LookLootDeviceTokenProvider = (context: LookLootDeviceTokenContext) => Promise<string> | string;
10
+ type LookLootOverwolfUploadBridge = {
11
+ uploadFile(input: {
12
+ filePath: string;
13
+ uploadUrl: string;
14
+ headers: Record<string, string>;
15
+ }): Promise<{
16
+ sizeBytes?: number;
17
+ checksumSha256?: string;
18
+ }> | {
19
+ sizeBytes?: number;
20
+ checksumSha256?: string;
21
+ };
22
+ };
23
+ type LookLootNativeInputSource = {
24
+ start?(): void | Promise<void>;
25
+ stop?(): void | Promise<void>;
26
+ readEvents?(): unknown[] | Promise<unknown[]>;
27
+ };
28
+ type LookLootNativeVideoUploadBackend = "overwolf_replay" | "chunked_hls";
29
+ type LookLootNativeVideoCaptureBackend = "overwolf_recorder" | "browser_display_media";
30
+ type LookLootBrowserDisplayMediaSource = {
31
+ stream: MediaStream;
32
+ label?: string;
33
+ displaySurface?: string;
34
+ };
35
+ type LookLootOverwolfNativeGame = {
36
+ overwolfGameId?: number;
37
+ rawOverwolfGameId?: number;
38
+ name: string;
39
+ processId?: number;
40
+ processName?: string;
41
+ processPath?: string;
42
+ windowHandle?: number;
43
+ windowTitle?: string;
44
+ commandLine?: string;
45
+ iconUrl?: string;
46
+ isForeground?: boolean;
47
+ detectedBy?: string;
48
+ detectionConfidence?: "low" | "medium" | "high";
49
+ };
50
+ type LookLootOverwolfNativeProcessRecord = {
51
+ ProcessId?: unknown;
52
+ processId?: unknown;
53
+ Name?: unknown;
54
+ name?: unknown;
55
+ ExecutablePath?: unknown;
56
+ executablePath?: unknown;
57
+ path?: unknown;
58
+ CommandLine?: unknown;
59
+ commandLine?: unknown;
60
+ CreationDate?: unknown;
61
+ creationDate?: unknown;
62
+ MainWindowTitle?: unknown;
63
+ mainWindowTitle?: unknown;
64
+ windowTitle?: unknown;
65
+ MainWindowHandle?: unknown;
66
+ mainWindowHandle?: unknown;
67
+ windowHandle?: unknown;
68
+ IsForeground?: unknown;
69
+ isForeground?: unknown;
70
+ };
71
+ type LookLootOverwolfNativeProcessPlugin = {
72
+ GetProcessListJson?: () => string;
73
+ getProcessListJson?: () => string;
74
+ GetStatusJson?: () => string;
75
+ getStatusJson?: () => string;
76
+ StartInputCaptureJson?: () => string;
77
+ startInputCaptureJson?: () => string;
78
+ StopInputCaptureJson?: () => string;
79
+ stopInputCaptureJson?: () => string;
80
+ ReadInputEventsJson?: () => string;
81
+ readInputEventsJson?: () => string;
82
+ };
83
+ type LookLootOverwolfNativeProcessPluginProvider = LookLootOverwolfNativeProcessPlugin | null | undefined | (() => LookLootOverwolfNativeProcessPlugin | null | undefined | Promise<LookLootOverwolfNativeProcessPlugin | null | undefined>);
84
+ type LookLootOverwolfNativeGameCatalogEntry = {
85
+ gameId?: string;
86
+ displayName: string;
87
+ processNames?: string[];
88
+ executableNames?: string[];
89
+ aliases?: string[];
90
+ iconUrl?: string;
91
+ };
92
+ type LookLootOverwolfNativeProcessOptions = {
93
+ /**
94
+ * Optional native extra-object/plugin provider. When omitted, the SDK tries
95
+ * `overwolf.extensions.current.getExtraObject(extraObjectId)`.
96
+ */
97
+ plugin?: LookLootOverwolfNativeProcessPluginProvider;
98
+ extraObjectId?: string;
99
+ gameCatalog?: LookLootOverwolfNativeGameCatalogEntry[];
100
+ installedGames?: Array<Record<string, unknown>>;
101
+ nonGameProcessNames?: string[];
102
+ nonGamePathHints?: string[];
103
+ nonGameWindowTitles?: string[];
104
+ };
105
+ type LookLootOverwolfGameWindowSourceOptions = {
106
+ game?: unknown;
107
+ frameRate?: number;
108
+ width?: number;
109
+ height?: number;
110
+ cursor?: "always" | "motion" | "never";
111
+ preferDirectWindowHandle?: boolean;
112
+ };
113
+ type LookLootOverwolfNativeInputSourceOptions = {
114
+ nativeProcess?: LookLootOverwolfNativeProcessOptions;
115
+ onEvents?: (events: unknown[]) => void;
116
+ };
117
+ type LookLootOverwolfNativeIntegrationOptions = LookLootCaptureOptions & {
118
+ nativeProcess?: LookLootOverwolfNativeProcessOptions;
119
+ gameWindowCapture?: LookLootOverwolfGameWindowSourceOptions;
120
+ };
121
+ type LookLootOverwolfNativeIntegration = {
122
+ capture: LookLootCapture;
123
+ init(): Promise<void>;
124
+ start(options?: LookLootCaptureStartOptions & {
125
+ gameWindowCapture?: LookLootOverwolfGameWindowSourceOptions;
126
+ }): Promise<{
127
+ ok: true;
128
+ } | {
129
+ ok: false;
130
+ reason: string;
131
+ }>;
132
+ stop(): Promise<{
133
+ ok: true;
134
+ sessionId: string | null;
135
+ uploaded: boolean;
136
+ }>;
137
+ dispose(): Promise<void>;
138
+ getState(): LookLootCaptureState;
139
+ getLiveInputEvents(limit?: number): InputEvent[];
140
+ getRunningGame(): Promise<LookLootOverwolfNativeGame | null>;
141
+ requestGameWindowSource(options?: LookLootOverwolfGameWindowSourceOptions): Promise<LookLootBrowserDisplayMediaSource>;
142
+ getRuntimeDiagnostics(): LookLootCaptureDiagnostics;
143
+ runLocalVerification(options?: LookLootCaptureLocalVerificationOptions): Promise<LookLootCaptureLocalVerificationResult>;
144
+ openLocalVerificationViewer(resultOrOptions?: LookLootCaptureLocalVerificationResult | LookLootCaptureLocalVerificationViewerOptions): Promise<LookLootCaptureLocalVerificationViewerResult>;
145
+ retryFailedUpload(): Promise<{
146
+ ok: true;
147
+ } | {
148
+ ok: false;
149
+ reason: string;
150
+ }>;
151
+ discardCaptureUpload(): void;
152
+ };
153
+ type LookLootCaptureUploadProgress = {
154
+ status: "idle" | "uploading" | "ready" | "failed";
155
+ videoUploadBackend: LookLootNativeVideoUploadBackend;
156
+ playbackUrl?: string | null;
157
+ uploadedSegmentCount?: number;
158
+ uploadedPlaylistKey?: string | null;
159
+ uploadedPlaylistUrl?: string | null;
160
+ reason?: string;
161
+ };
162
+ type LookLootCaptureOptions = {
163
+ appId: string;
164
+ appVersion?: string;
165
+ getDeviceToken: LookLootDeviceTokenProvider;
166
+ apiBaseUrl?: string;
167
+ deviceName?: string;
168
+ anonymousId?: string;
169
+ autoStartOnGame?: boolean;
170
+ uploadBridge?: LookLootOverwolfUploadBridge;
171
+ replay?: {
172
+ /**
173
+ * `overwolf_recorder` uses Overwolf Native recording APIs and is the
174
+ * default no-OBS path for Overwolf-supported recording contexts.
175
+ * `browser_display_media` uses Chromium/Windows window capture through
176
+ * getDisplayMedia and rejects monitor/browser-tab selections.
177
+ */
178
+ captureBackend?: LookLootNativeVideoCaptureBackend;
179
+ bufferLengthMs?: number;
180
+ quotaGb?: number;
181
+ gameWindowCapture?: boolean;
182
+ captureMouseCursor?: "both" | "game" | "none";
183
+ desktopCapture?: boolean | {
184
+ enable?: boolean;
185
+ forceCapture?: boolean;
186
+ monitorId?: number;
187
+ };
188
+ };
189
+ gep?: {
190
+ features?: string[];
191
+ };
192
+ input?: {
193
+ nativeSource?: LookLootNativeInputSource;
194
+ pollIntervalMs?: number;
195
+ };
196
+ upload?: {
197
+ /**
198
+ * `chunked_hls` mirrors the Electron upload path by uploading closed
199
+ * Overwolf recorder chunks and a growing playlist during capture.
200
+ * `overwolf_replay` uploads one finalized MP4 after stop.
201
+ */
202
+ videoBackend?: LookLootNativeVideoUploadBackend;
203
+ };
204
+ };
205
+ type LookLootCaptureStartOptions = {
206
+ game?: unknown;
207
+ browserDisplayMedia?: LookLootBrowserDisplayMediaSource | MediaStream;
208
+ };
209
+ type LookLootCaptureState = "idle" | "active";
210
+ type LookLootCaptureRuntimeCheck = {
211
+ ok: boolean;
212
+ required: boolean;
213
+ reason?: string;
214
+ };
215
+ type LookLootCaptureDiagnostics = {
216
+ ok: boolean;
217
+ platform: "overwolf-native";
218
+ checks: {
219
+ overwolf: LookLootCaptureRuntimeCheck;
220
+ games: LookLootCaptureRuntimeCheck;
221
+ replays: LookLootCaptureRuntimeCheck;
222
+ streaming: LookLootCaptureRuntimeCheck;
223
+ inputTracking: LookLootCaptureRuntimeCheck;
224
+ gep: LookLootCaptureRuntimeCheck;
225
+ uploadBridge: LookLootCaptureRuntimeCheck;
226
+ queue: LookLootCaptureRuntimeCheck;
227
+ };
228
+ };
229
+ type LookLootCaptureLocalVerificationOptions = {
230
+ /**
231
+ * How long the local replay capture should run after a game is detected.
232
+ * Defaults to 8000 ms.
233
+ */
234
+ durationMs?: number;
235
+ /**
236
+ * Optional explicit Overwolf game event/info object. Omit this to use
237
+ * overwolf.games.getRunningGameInfo2/getRunningGameInfo.
238
+ */
239
+ game?: unknown;
240
+ /**
241
+ * Whether result.ok requires replay video evidence. Defaults to true.
242
+ */
243
+ requireVideo?: boolean;
244
+ /**
245
+ * Whether result.ok requires at least one keyboard/mouse input event.
246
+ * Defaults to true.
247
+ */
248
+ requireInput?: boolean;
249
+ /**
250
+ * Whether result.ok requires at least one GEP event/snapshot. Defaults to
251
+ * false because many games have no configured GEP features.
252
+ */
253
+ requireGep?: boolean;
254
+ };
255
+ type LookLootCaptureLocalVerificationGame = {
256
+ overwolfGameId?: number;
257
+ rawOverwolfGameId?: number;
258
+ name: string;
259
+ processId?: number;
260
+ processName?: string;
261
+ commandLine?: string;
262
+ };
263
+ type LookLootCaptureLocalVerificationResult = {
264
+ ok: boolean;
265
+ reason?: string;
266
+ durationMs: number;
267
+ diagnostics: LookLootCaptureDiagnostics;
268
+ game?: LookLootCaptureLocalVerificationGame;
269
+ video: {
270
+ ok: boolean;
271
+ replayId?: string;
272
+ mediaPath?: string;
273
+ mediaUrl?: string;
274
+ reason?: string;
275
+ };
276
+ input: {
277
+ ok: boolean;
278
+ eventCount: number;
279
+ reason?: string;
280
+ };
281
+ gameState: {
282
+ ok: boolean;
283
+ eventCount: number;
284
+ snapshotCount: number;
285
+ required: boolean;
286
+ features: string[];
287
+ reason?: string;
288
+ };
289
+ upload: {
290
+ attempted: false;
291
+ reason: string;
292
+ };
293
+ };
294
+ type LookLootCaptureLocalVerificationViewerOptions = {
295
+ /**
296
+ * Verification result returned by runLocalVerification(). When omitted, the
297
+ * most recent result from this instance is used.
298
+ */
299
+ result?: LookLootCaptureLocalVerificationResult;
300
+ /**
301
+ * Open the generated in-memory report in a browser window. Defaults to true.
302
+ */
303
+ open?: boolean;
304
+ /**
305
+ * Report title.
306
+ */
307
+ title?: string;
308
+ };
309
+ type LookLootCaptureLocalVerificationViewerResult = {
310
+ ok: boolean;
311
+ opened: boolean;
312
+ url?: string;
313
+ html?: string;
314
+ reason?: string;
315
+ };
316
+ type Events = {
317
+ "state-change": (state: LookLootCaptureState) => void;
318
+ "upload-progress": (progress: LookLootCaptureUploadProgress) => void;
319
+ error: (error: Error) => void;
320
+ };
321
+ type Listener<T extends (...args: any[]) => unknown> = T;
322
+ declare class TinyEmitter<E extends {
323
+ [K in keyof E]: (...args: any[]) => unknown;
324
+ }> {
325
+ private eventListeners;
326
+ on<K extends keyof E>(event: K, listener: Listener<E[K]>): this;
327
+ off<K extends keyof E>(event: K, listener: Listener<E[K]>): this;
328
+ once<K extends keyof E>(event: K, listener: Listener<E[K]>): this;
329
+ emit<K extends keyof E>(event: K, ...args: Parameters<E[K]>): void;
330
+ removeAllListeners(): void;
331
+ }
332
+ interface LookLootCapture {
333
+ on<K extends keyof Events>(event: K, listener: Events[K]): this;
334
+ off<K extends keyof Events>(event: K, listener: Events[K]): this;
335
+ once<K extends keyof Events>(event: K, listener: Events[K]): this;
336
+ }
337
+ declare class LookLootCapture extends TinyEmitter<Events> {
338
+ private readonly appId;
339
+ private readonly appVersion;
340
+ private readonly deviceName;
341
+ private readonly anonymousId;
342
+ private readonly getDeviceToken;
343
+ private readonly apiBaseUrl;
344
+ private readonly autoStartOnGame;
345
+ private readonly replayOptions;
346
+ private readonly desktopCaptureOptions;
347
+ private readonly nativeInputSource;
348
+ private readonly nativeInputPollIntervalMs;
349
+ private readonly gepFeatures;
350
+ private readonly videoUploadBackend;
351
+ private readonly videoCaptureBackend;
352
+ private uploadBridge;
353
+ private token;
354
+ private initialized;
355
+ private disposed;
356
+ private active;
357
+ private state;
358
+ private removeListeners;
359
+ private lastGame;
360
+ private lastLocalVerification;
361
+ private nativeInputPollTimer;
362
+ constructor(options: LookLootCaptureOptions);
363
+ init(): Promise<void>;
364
+ start(options?: LookLootCaptureStartOptions): Promise<{
365
+ ok: true;
366
+ } | {
367
+ ok: false;
368
+ reason: string;
369
+ }>;
370
+ stop(): Promise<{
371
+ ok: true;
372
+ sessionId: string | null;
373
+ uploaded: boolean;
374
+ }>;
375
+ dispose(): Promise<void>;
376
+ getState(): LookLootCaptureState;
377
+ getLiveInputEvents(limit?: number): InputEvent[];
378
+ getRuntimeDiagnostics(): LookLootCaptureDiagnostics;
379
+ runLocalVerification(options?: LookLootCaptureLocalVerificationOptions): Promise<LookLootCaptureLocalVerificationResult>;
380
+ openLocalVerificationViewer(resultOrOptions?: LookLootCaptureLocalVerificationResult | LookLootCaptureLocalVerificationViewerOptions): Promise<LookLootCaptureLocalVerificationViewerResult>;
381
+ retryFailedUpload(): Promise<{
382
+ ok: true;
383
+ } | {
384
+ ok: false;
385
+ reason: string;
386
+ }>;
387
+ discardCaptureUpload(): void;
388
+ private getOverwolf;
389
+ private requireOverwolf;
390
+ private resolveDeviceToken;
391
+ private installListeners;
392
+ private addOwListener;
393
+ private addInputListeners;
394
+ private addGepListeners;
395
+ private initializeInputTracking;
396
+ private turnOnReplay;
397
+ private configureGep;
398
+ requestBrowserDisplayMedia(options?: {
399
+ game?: unknown;
400
+ }): Promise<LookLootBrowserDisplayMediaSource>;
401
+ private startBrowserDisplayMediaRecorder;
402
+ private startVideoCapture;
403
+ private stopVideoCapture;
404
+ private useStreamingRecorder;
405
+ private useBrowserDisplayMedia;
406
+ private desktopCaptureSettings;
407
+ private streamingSettings;
408
+ private replaySettings;
409
+ private getRunningGame;
410
+ private createSession;
411
+ private recordInput;
412
+ private startNativeInputCapture;
413
+ private stopNativeInputCapture;
414
+ private pollNativeInputEvents;
415
+ private recordNativeInput;
416
+ private recordNativeFocusReset;
417
+ private recordGep;
418
+ private captureGepSnapshot;
419
+ private relativeMs;
420
+ private handleVideoFileSplit;
421
+ private handleStreamingSourceImageChanged;
422
+ private enqueueNativeChunkUpload;
423
+ private enqueueNativeBlobChunkUpload;
424
+ private uploadNativeVideoChunk;
425
+ private uploadNativeVideoBlobChunk;
426
+ private uploadNativePlaylist;
427
+ private uploadFinalNativeChunk;
428
+ private finishNativeChunkedVideo;
429
+ private checkpointNativeChunkedVideo;
430
+ private emitUploadProgress;
431
+ private uploadFinalizedSession;
432
+ private createManifest;
433
+ private uploadVideo;
434
+ private uploadText;
435
+ private presign;
436
+ private presignBatch;
437
+ private putText;
438
+ private putBlob;
439
+ private completeUpload;
440
+ private apiPost;
441
+ private queueSession;
442
+ private removeQueuedSession;
443
+ private setState;
444
+ private emitError;
445
+ private localVerificationBase;
446
+ }
447
+ declare function createLookLootOverwolfNativeIntegration(options: LookLootOverwolfNativeIntegrationOptions): LookLootOverwolfNativeIntegration;
448
+ declare function getLookLootOverwolfRunningGame(options?: {
449
+ nativeProcess?: LookLootOverwolfNativeProcessOptions;
450
+ }): Promise<LookLootOverwolfNativeGame | null>;
451
+ declare function requestLookLootOverwolfGameWindowSource(options?: LookLootOverwolfGameWindowSourceOptions): Promise<LookLootBrowserDisplayMediaSource>;
452
+ declare function resolveLookLootOverwolfNativeProcessPlugin(options?: LookLootOverwolfNativeProcessOptions): Promise<LookLootOverwolfNativeProcessPlugin | null>;
453
+ declare function createLookLootOverwolfNativeInputSource(options?: LookLootOverwolfNativeInputSourceOptions): LookLootNativeInputSource;
454
+ declare function detectLookLootOverwolfNativeProcessGames(processes: LookLootOverwolfNativeProcessRecord[], options?: LookLootOverwolfNativeProcessOptions): LookLootOverwolfNativeGame[];
455
+
456
+ export { type LookLootBrowserDisplayMediaSource, LookLootCapture, type LookLootCaptureDiagnostics, type LookLootCaptureLocalVerificationGame, type LookLootCaptureLocalVerificationOptions, type LookLootCaptureLocalVerificationResult, type LookLootCaptureLocalVerificationViewerOptions, type LookLootCaptureLocalVerificationViewerResult, type LookLootCaptureOptions, type LookLootCaptureRuntimeCheck, type LookLootCaptureStartOptions, type LookLootCaptureState, type LookLootCaptureUploadProgress, type LookLootDeviceTokenContext, type LookLootDeviceTokenProvider, type LookLootNativeInputSource, type LookLootNativeVideoCaptureBackend, type LookLootNativeVideoUploadBackend, type LookLootOverwolfGameWindowSourceOptions, type LookLootOverwolfNativeGame, type LookLootOverwolfNativeGameCatalogEntry, type LookLootOverwolfNativeInputSourceOptions, type LookLootOverwolfNativeIntegration, type LookLootOverwolfNativeIntegrationOptions, type LookLootOverwolfNativeProcessOptions, type LookLootOverwolfNativeProcessPlugin, type LookLootOverwolfNativeProcessPluginProvider, type LookLootOverwolfNativeProcessRecord, type LookLootOverwolfUploadBridge, createLookLootOverwolfNativeInputSource, createLookLootOverwolfNativeIntegration, detectLookLootOverwolfNativeProcessGames, getLookLootOverwolfRunningGame, requestLookLootOverwolfGameWindowSource, resolveLookLootOverwolfNativeProcessPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,46 @@
1
+ function se(t=new Date){return t.toISOString()}function de(t){let e=typeof t=="number"?t:Number(t);return!Number.isFinite(e)||e<0?0:Math.round(e)}function Ve(t){if(typeof t!="string")return t;let e=t.trim();if(!e||!/^[\[{"]/.test(e))return t;try{return JSON.parse(e)}catch{return t}}function le(t){return{sessionId:t.sessionId,segmentId:t.segmentId,atMs:de(t.atMs),receivedAt:t.receivedAt??se(),source:t.source,gameId:t.gameId,feature:t.feature,category:t.category,key:t.key,name:t.name,normalized:t.normalized,raw:Ve(t.raw)}}function U(t){return{sessionId:t.sessionId,segmentId:t.segmentId,atMs:de(t.atMs),receivedAt:t.receivedAt??se(),source:t.source,deviceType:t.deviceType,state:t.state,action:t.action,keyCode:t.keyCode,virtualKey:t.virtualKey,scanCode:t.scanCode,extended:t.extended,mouseButton:t.mouseButton,wheelDelta:t.wheelDelta,movementDelta:t.movementDelta,gamepadButton:t.gamepadButton,gamepadAxis:t.gamepadAxis,foregroundProcess:t.foregroundProcess,foregroundProcessId:t.foregroundProcessId,foregroundTitle:t.foregroundTitle,captureMethod:t.captureMethod,nativeTimestampQpc:t.nativeTimestampQpc,nativeTimestampFrequency:t.nativeTimestampFrequency,nativeTimestampMs:t.nativeTimestampMs,sourcePerfMs:t.sourcePerfMs,inputDelayMs:t.inputDelayMs,modifiers:t.modifiers}}function ue(t){return{...t,schemaVersion:1,retentionPolicy:"keep_until_deleted",inputPolicy:"gameplay_actions_with_fallback_keys"}}var Fe="https://www.lookloot.gg",Se="lookloot:overwolf-native:pending-sessions:v1",ce="lookloot:overwolf-native:anonymous-id:v1",ze=3e6,V=4e3,Ie=4e3,Ke=6e6,Me="video/webm",Z=class{eventListeners=new Map;on(e,o){let n=this.eventListeners.get(e)??new Set;return n.add(o),this.eventListeners.set(e,n),this}off(e,o){return this.eventListeners.get(e)?.delete(o),this}once(e,o){let n=((...r)=>{this.off(e,n),o(...r)});return this.on(e,n)}emit(e,...o){for(let n of this.eventListeners.get(e)??[])n(...o)}removeAllListeners(){this.eventListeners.clear()}},ee=class extends Z{appId;appVersion;deviceName;anonymousId;getDeviceToken;apiBaseUrl;autoStartOnGame;replayOptions;desktopCaptureOptions;nativeInputSource;nativeInputPollIntervalMs;gepFeatures;videoUploadBackend;videoCaptureBackend;uploadBridge;token=null;initialized=!1;disposed=!1;active=null;state="idle";removeListeners=[];lastGame=null;lastLocalVerification=null;nativeInputPollTimer=null;constructor(e){if(super(),typeof e.getDeviceToken!="function")throw new Error("getDeviceToken is required");let o=e.appId?.trim().toLowerCase();if(!o)throw new Error("appId is required");if(this.appId=o,this.appVersion=e.appVersion?.trim()||void 0,this.deviceName=e.deviceName?.trim()||void 0,this.anonymousId=Vt(e.anonymousId),this.getDeviceToken=e.getDeviceToken,this.apiBaseUrl=(e.apiBaseUrl??Fe).replace(/\/$/,""),this.autoStartOnGame=e.autoStartOnGame??!0,this.replayOptions={bufferLengthMs:M(e.replay?.bufferLengthMs,1e4),quotaGb:M(e.replay?.quotaGb,5),gameWindowCapture:e.replay?.gameWindowCapture??!0,captureMouseCursor:e.replay?.captureMouseCursor??"both",desktopCapture:e.replay?.desktopCapture??!1},this.desktopCaptureOptions=ht(e.replay?.desktopCapture),this.nativeInputSource=e.input?.nativeSource,this.nativeInputPollIntervalMs=M(e.input?.pollIntervalMs,100),this.gepFeatures=wt(e.gep?.features??[]),this.videoUploadBackend=e.upload?.videoBackend==="chunked_hls"?"chunked_hls":"overwolf_replay",this.videoCaptureBackend=e.replay?.captureBackend==="browser_display_media"?"browser_display_media":"overwolf_recorder",this.videoCaptureBackend==="browser_display_media"&&this.videoUploadBackend!=="chunked_hls")throw new Error("browser_display_media capture requires upload.videoBackend to be chunked_hls");this.uploadBridge=e.uploadBridge??D()}async init(){if(this.disposed)throw new Error("LookLootCapture has been disposed");let e=this.requireOverwolf();this.token=await this.resolveDeviceToken(),this.installListeners(e),await this.initializeInputTracking(e),this.videoCaptureBackend==="overwolf_recorder"&&!this.useStreamingRecorder()&&await this.turnOnReplay(e),await this.configureGep(e),this.initialized=!0,await this.retryFailedUpload();let o=await this.getRunningGame(e);o&&(this.lastGame=o,this.autoStartOnGame&&this.start({game:o}).catch(n=>this.emitError(n)))}async start(e={}){if(!this.initialized||!this.token)return{ok:!1,reason:"Call init() first"};if(this.active)return{ok:!0};let o=this.requireOverwolf(),n=b(e?.game)??this.lastGame??await this.getRunningGame(o);if(!n)return{ok:!1,reason:"No running Overwolf game is detected yet."};let r=null;try{r=this.useBrowserDisplayMedia()?Ue(e.browserDisplayMedia)??await this.requestBrowserDisplayMedia({game:n}):null;let a=await this.createSession(n),i=await this.startVideoCapture(o,r);return this.active={serverSessionId:a.sessionId,rootSegmentId:a.rootSegmentId,game:n,videoUploadBackend:this.videoUploadBackend,videoBackend:i.backend,chunkedVideo:this.videoUploadBackend==="chunked_hls"?vt(i.backend==="browser_display_media"?"browser_display_media":"overwolf_native_split"):null,browserDisplayMedia:i.browserDisplayMedia??null,replayId:i.replayId,streamId:i.streamId,captureStartResult:i.result,startedAt:new Date().toISOString(),startedWallMs:Date.now(),inputEvents:[],nativeHeldInputKeys:new Set,gameStateEvents:[]},this.active.browserDisplayMedia&&this.startBrowserDisplayMediaRecorder(this.active),await this.startNativeInputCapture(),this.setState("active"),this.emitUploadProgress({status:"idle"}),await this.captureGepSnapshot(),{ok:!0}}catch(a){if(this.active&&this.state!=="active"){let i=this.active;this.active=null,_(i.browserDisplayMedia)}else this.active||_(r);return this.emitError(a),{ok:!1,reason:I(a)}}}async stop(){if(!this.active)return{ok:!0,sessionId:null,uploaded:!1};let e=this.active;this.active=null;let o=this.requireOverwolf(),n=await this.stopVideoCapture(o,e);if(await this.stopNativeInputCapture(e),e.abortReason)throw this.setState("idle"),new Error(e.abortReason);let r=K(n);if(!r)throw new Error("Overwolf replay stop did not return media_path/path");await this.uploadFinalNativeChunk(e,n),await this.finishNativeChunkedVideo(e);let a=new Date().toISOString(),i={id:`${e.serverSessionId}:${Date.now()}`,serverSessionId:e.serverSessionId,rootSegmentId:e.rootSegmentId,game:e.game,replay:{mediaUrl:te(n),mediaPath:r,backend:e.videoBackend,durationMs:u(n.duration),displaySurface:e.browserDisplayMedia?.displaySurface,label:e.browserDisplayMedia?.label},startedAt:e.startedAt,endedAt:a,durationMs:Math.max(0,Date.now()-e.startedWallMs),videoUploadBackend:e.videoUploadBackend,chunkedVideo:e.chunkedVideo??null,inputEvents:e.inputEvents,gameStateEvents:e.gameStateEvents};this.setState("idle");let s=!0;return await this.uploadFinalizedSession(i).catch(async d=>{s=!1,await this.queueSession(i,I(d)),this.emitError(d)}),{ok:!0,sessionId:e.serverSessionId,uploaded:s}}async dispose(){if(!this.disposed){this.disposed=!0,await this.stop().catch(e=>this.emitError(e)),this.nativeInputPollTimer!==null&&(clearInterval(this.nativeInputPollTimer),this.nativeInputPollTimer=null);for(let e of this.removeListeners.splice(0))e();await h(e=>this.getOverwolf()?.media?.replays?.turnOff?.(e)).catch(()=>{}),this.removeAllListeners()}}getState(){return this.state}getLiveInputEvents(e=400){let o=this.active?.inputEvents??[],n=typeof e=="number"&&Number.isFinite(e)&&e>0?Math.floor(e):400;return o.slice(-n).map(r=>({...r}))}getRuntimeDiagnostics(){let e=this.getOverwolf(),o=this.uploadBridge??D(),n=this.gepFeatures.length>0,r=this.videoCaptureBackend==="overwolf_recorder",a=this.useStreamingRecorder(),i=this.videoCaptureBackend==="overwolf_recorder",s=e?.games?.events,d={overwolf:S(!!e,!0,"window.overwolf is not available."),games:S(!!(e?.games?.getRunningGameInfo2||e?.games?.getRunningGameInfo||e?.games?.tracked?.getRunningGameInfo||e?.games?.tracked?.getAnyRunningGamesInfo),!0,"overwolf.games running-game APIs are not available."),replays:S(!!(e?.media?.replays?.turnOn&&e.media.replays.startCapture&&e.media.replays.stopCapture),r,this.videoCaptureBackend==="browser_display_media"?"Browser display-media capture is configured; Overwolf replay APIs are not required.":"overwolf.media.replays capture APIs are not available."),streaming:S(!!(e?.streaming?.start&&e.streaming.stop),a,a?"overwolf.streaming start/stop APIs are not available for chunked recorder uploads.":"Streaming recorder is not required for this upload mode."),inputTracking:S(!!(e?.games?.inputTracking||this.nativeInputSource?.readEvents),!0,"overwolf.games.inputTracking is not available and no native input source is registered."),gep:S(!!(s&&(!n||s.setRequiredFeatures&&s.getInfo)&&(s.onNewEvents||s.onInfoUpdates2||s.getInfo)),n,"overwolf.games.events GEP APIs are not available."),uploadBridge:S(!!o?.uploadFile,i,this.videoCaptureBackend==="browser_display_media"?"Browser display-media capture uploads Blob chunks directly; the file upload bridge is not required.":"LookLoot Overwolf Native upload bridge is not registered."),queue:S(At(),!1,"localStorage is not available; failed uploads cannot persist.")};return{ok:Object.values(d).every(c=>c.ok),platform:"overwolf-native",checks:d}}async runLocalVerification(e={}){if(this.disposed)throw new Error("LookLootCapture has been disposed");if(this.active){let f=this.localVerificationBase(he(e.durationMs),this.getRuntimeDiagnostics(),"A capture is already active. Stop it before running local verification.");return this.lastLocalVerification=f,f}let o=he(e.durationMs),n=this.getRuntimeDiagnostics(),r=e.requireVideo!==!1,a=e.requireInput!==!1,i=e.requireGep===!0,s=f=>this.localVerificationBase(o,n,f),d=gt(n,{hasExplicitGame:!!e.game,requireInput:a,requireGep:i});if(d.length>0){let f={...s(d[0]),video:{ok:!1,reason:d[0]},input:{ok:!1,eventCount:0,reason:d[0]},gameState:{ok:!1,eventCount:0,snapshotCount:0,required:i,features:[...this.gepFeatures],reason:i?d[0]:void 0}};return this.lastLocalVerification=f,f}let c=this.requireOverwolf(),m=this.removeListeners.length,w=this.initialized,y=!1,g=null;try{this.installListeners(c),await this.initializeInputTracking(c),await this.turnOnReplay(c),y=!0,await this.configureGep(c);let f=b(e.game)??await this.getRunningGame(c);if(!f){let C={...s("No running Overwolf game is detected. Open a game, focus it, and run verification again."),video:{ok:!1,reason:"No running Overwolf game is detected."},input:{ok:!1,eventCount:0,reason:"No running Overwolf game is detected."}};return this.lastLocalVerification=C,C}let v=await this.startVideoCapture(c,null,{forceOverwolfReplay:!0});g={serverSessionId:`local-verification-${Date.now().toString(36)}`,rootSegmentId:"local-verification-root",game:f,videoUploadBackend:"overwolf_replay",videoBackend:v.backend,chunkedVideo:null,replayId:v.replayId,streamId:v.streamId,captureStartResult:v.result,startedAt:new Date().toISOString(),startedWallMs:Date.now(),inputEvents:[],nativeHeldInputKeys:new Set,gameStateEvents:[]},this.active=g,await this.startNativeInputCapture(),this.setState("active"),await this.captureGepSnapshot(),await mt(o);let L=g,T=await this.stopVideoCapture(c,L);await this.stopNativeInputCapture(L);let ne=K(T),re=te(T),q=!!(ne||re),R=L.inputEvents.length,B=L.gameStateEvents.length,Ae=L.gameStateEvents.filter(C=>C.source==="overwolf_gep_snapshot").length,ae=[r&&!q?"Overwolf replay did not return media_path/path or media_url/url.":null,a&&R<=0?"No keyboard or mouse input was captured. Focus the game and press keys or click during verification.":null,i&&B<=0?"No Overwolf GEP events or snapshots were captured.":null].filter(C=>!!C),ie={ok:ae.length===0,reason:ae[0],durationMs:o,diagnostics:n,game:ft(f),video:{ok:q,replayId:v.replayId??(v.streamId!==void 0?`stream-${v.streamId}`:void 0),mediaPath:ne,mediaUrl:re,reason:q?void 0:"Overwolf capture did not return media evidence."},input:{ok:R>0,eventCount:R,reason:R>0?void 0:"No keyboard or mouse input was captured."},gameState:{ok:B>0,eventCount:B,snapshotCount:Ae,required:i,features:[...this.gepFeatures],reason:B>0?void 0:i?"No Overwolf GEP events or snapshots were captured.":void 0},upload:we()};return this.lastLocalVerification=ie,ie}catch(f){let v=I(f),L={...s(v),video:{ok:!1,reason:v},input:{ok:!1,eventCount:g?.inputEvents.length??0,reason:v},gameState:{ok:!1,eventCount:g?.gameStateEvents.length??0,snapshotCount:g?.gameStateEvents.filter(T=>T.source==="overwolf_gep_snapshot").length??0,required:i,features:[...this.gepFeatures],reason:i?v:void 0}};return this.lastLocalVerification=L,L}finally{g&&await this.stopNativeInputCapture(g).catch(()=>{}),this.active===g&&(this.active=null),this.setState("idle"),!w&&y&&await h(f=>c.media?.replays?.turnOff?.(f)).catch(()=>{});for(let f of this.removeListeners.splice(m))f()}}async openLocalVerificationViewer(e){let o=xt(e)?{result:e}:e??{},n=o.result??this.lastLocalVerification;if(!n)return{ok:!1,opened:!1,reason:"No local verification result was found. Run runLocalVerification() first."};let r=Ut(n,o.title);if(o.open===!1)return{ok:!0,opened:!1,html:r};let a=Tt();if(!a?.open)return{ok:!1,opened:!1,html:r,reason:"window.open is not available in this Overwolf context."};let i=Rt(r)??Bt(r);return a.open(i,"_blank","noopener,noreferrer")?{ok:!0,opened:!0,url:i}:{ok:!1,opened:!1,url:i,html:r,reason:"The verification report window was blocked."}}async retryFailedUpload(){let e=Q();if(!e.length)return{ok:!0};let o=[];for(let n of e)try{await this.uploadFinalizedSession(n)}catch(r){o.push({...n,attempts:n.attempts+1,lastError:I(r)})}return A(o),o.length?{ok:!1,reason:`${o.length} capture upload(s) still pending.`}:{ok:!0}}discardCaptureUpload(){A([])}getOverwolf(){let e=globalThis;return e.window?.overwolf??e.overwolf??null}requireOverwolf(){let e=this.getOverwolf();if(!e)throw new Error("LookLoot Overwolf Native SDK requires window.overwolf");return e}async resolveDeviceToken(){let e;try{e=await this.getDeviceToken({appId:this.appId,appVersion:this.appVersion,deviceName:this.deviceName,anonymousId:this.anonymousId})}catch(o){throw new Error("LookLoot SDK init: could not get device token",{cause:o})}if(typeof e!="string"||!e.trim())throw new Error("LookLoot SDK init: getDeviceToken returned no token");return e.trim()}installListeners(e){this.removeListeners.length||(this.addOwListener(e.games?.onGameLaunched,o=>{let n=b(o);n&&(this.lastGame=n,this.initialized&&this.autoStartOnGame&&!this.active&&this.start({game:n}).catch(r=>this.emitError(r)))}),this.addOwListener(e.games?.onGameInfoUpdated,o=>{let n=b(o),r=xe(o);n&&(this.lastGame=n),r===!1&&this.active&&Y(this.active.game,n)?this.stop().catch(a=>this.emitError(a)):n&&r!==!1&&this.initialized&&this.autoStartOnGame&&!this.active&&this.start({game:n}).catch(a=>this.emitError(a))}),this.addOwListener(e.games?.tracked?.onUnsupportedExecuted,o=>{let n=P(o)??b(o);n&&(this.lastGame=n,this.initialized&&this.autoStartOnGame&&!this.active&&this.start({game:n}).catch(r=>this.emitError(r)))}),this.addOwListener(e.games?.tracked?.onTerminated,o=>{let n=b(o);this.active&&Y(this.active.game,n)?this.stop().catch(r=>this.emitError(r)):!this.active&&(!n||this.lastGame&&Y(this.lastGame,n))&&(this.lastGame=null)}),this.addOwListener(e.media?.replays?.onCaptureError,o=>{this.emitError(new Error(`Overwolf replay capture error: ${JSON.stringify(o)}`))}),this.addOwListener(e.streaming?.onStreamingError,o=>{this.emitError(new Error(`Overwolf streaming capture error: ${JSON.stringify(o)}`))}),this.addOwListener(e.streaming?.onStreamingSourceImageChanged,o=>{this.handleStreamingSourceImageChanged(o).catch(n=>this.emitError(n))}),this.addOwListener(e.streaming?.onVideoFileSplit,o=>{this.handleVideoFileSplit(o).catch(n=>this.emitError(n))}),this.addInputListeners(e),this.addGepListeners(e))}addOwListener(e,o){e?.addListener?.(o),e?.removeListener&&this.removeListeners.push(()=>e.removeListener?.(o))}addInputListeners(e){let o=e.games?.inputTracking;this.addOwListener(o?.onKeyDown,n=>this.recordInput(n,"keyboard","pressed")),this.addOwListener(o?.onKeyUp,n=>this.recordInput(n,"keyboard","released")),this.addOwListener(o?.onMouseDown,n=>this.recordInput(n,"mouse","pressed")),this.addOwListener(o?.onMouseUp,n=>this.recordInput(n,"mouse","released")),this.addOwListener(o?.onMouseWheel,n=>this.recordInput(n,"mouse","repeat"))}addGepListeners(e){let o=e.games?.events;this.addOwListener(o?.onNewEvents,n=>this.recordGep(n,"overwolf_gep_event")),this.addOwListener(o?.onInfoUpdates2,n=>this.recordGep(n,"overwolf_gep_info")),this.addOwListener(o?.onError,n=>{this.emitError(new Error(`Overwolf GEP error: ${JSON.stringify(n)}`))})}async initializeInputTracking(e){if(!e.games?.inputTracking?.init)return;let o=await h(n=>e.games?.inputTracking?.init?.(n));O(o,"Overwolf input tracking init failed")}async turnOnReplay(e){let o=e.media?.replays;if(!o?.turnOn)throw new Error("overwolf.media.replays.turnOn is not available");let n=await h(r=>o.turnOn?.(this.replaySettings(),r));O(n,"Overwolf replay turnOn failed")}async configureGep(e){if(!this.gepFeatures.length||!e.games?.events?.setRequiredFeatures)return;let o=await h(n=>e.games?.events?.setRequiredFeatures?.(this.gepFeatures,n));O(o,"Overwolf GEP setRequiredFeatures failed")}async requestBrowserDisplayMedia(e={}){let o=globalThis.navigator?.mediaDevices;if(!o?.getDisplayMedia)throw new Error("Browser window capture is not available in this Overwolf runtime.");let n=await o.getDisplayMedia({video:{frameRate:{ideal:30},width:{ideal:1280},height:{ideal:720}},audio:!1});try{return x(n,b(e.game))}catch(r){throw _({stream:n}),r}}startBrowserDisplayMediaRecorder(e){let o=e.browserDisplayMedia,n=e.chunkedVideo;if(!o||!n)return;let[r]=o.stream.getVideoTracks();if(!r||r.readyState!=="live")throw new Error(`Browser game-window capture ended before recording started (${r?.readyState??"missing"}).`);let a=new MediaRecorder(o.stream,{mimeType:o.mimeType,videoBitsPerSecond:Ke});o.recorder=a,o.lastSegmentWallMs=Date.now(),r.addEventListener("ended",()=>{let i="Browser game-window capture ended unexpectedly.";e.abortReason??=i,n.status="failed",n.lastError=i,this.emitUploadProgress({status:"failed",reason:i})},{once:!0}),a.addEventListener("dataavailable",i=>{if(!i.data||i.data.size<=0)return;let s=Date.now(),d=Math.max(1,s-o.lastSegmentWallMs);o.lastSegmentWallMs=s,this.enqueueNativeBlobChunkUpload(e,{blob:i.data,index:o.nextSegmentIndex++,durationMs:d})}),a.addEventListener("stop",()=>{o.resolveStopped({success:!0,status:"success",media_path:`browser-display-media:${o.label??e.game.name}`,path:`browser-display-media:${o.label??e.game.name}`,duration:Math.max(0,Date.now()-e.startedWallMs)})}),a.addEventListener("error",i=>{let s="error"in i&&i.error instanceof Error?i.error:new Error("Browser display media recorder failed.");n.status="failed",n.lastError=s.message,o.rejectStopped(s),this.emitUploadProgress({status:"failed",reason:s.message})}),a.start(Ie)}async startVideoCapture(e,o,n={}){if(!n.forceOverwolfReplay&&this.useBrowserDisplayMedia()){if(!o)throw new Error("Browser display media was not prepared for capture.");let i=yt(o);return{backend:"browser_display_media",result:{success:!0,status:"success",media_path:`browser-display-media:${i.label??"window"}`,path:`browser-display-media:${i.label??"window"}`},browserDisplayMedia:i}}if(!n.forceOverwolfReplay&&this.useStreamingRecorder()){let i=e.streaming;if(!i?.start)throw new Error("overwolf.streaming.start is not available for desktop capture");let s=await h(c=>i.start?.(this.streamingSettings(e),c));O(s,"Overwolf streaming start failed");let d=X(s);if(d===void 0)throw new Error("Overwolf streaming start did not return a stream id");return{backend:"overwolf_streaming",result:s,streamId:d}}let r=await h(i=>e.media?.replays?.startCapture?.(0,i));O(r,"Overwolf replay start failed");let a=ct(r);if(!a)throw new Error("Overwolf replay start did not return a replay id");return{backend:"overwolf_replay",result:r,replayId:a}}async stopVideoCapture(e,o){if(o.videoBackend==="browser_display_media"){let r=o.browserDisplayMedia;return r?.recorder?(r.recorder.state!=="inactive"&&r.recorder.stop(),r.stopped.finally(()=>_(r))):(_(r),{success:!0,status:"success",media_path:`browser-display-media:${r?.label??o.game.name}`,path:`browser-display-media:${r?.label??o.game.name}`})}if(o.videoBackend==="overwolf_streaming"){if(o.streamId===void 0)throw new Error("Overwolf streaming capture has no stream id");let r=await h(a=>e.streaming?.stop?.(o.streamId,a)).catch(a=>{throw new Error(`Overwolf streaming stop failed: ${I(a)}`)});return O(r,"Overwolf streaming stop failed"),r}if(!o.replayId)throw new Error("Overwolf replay capture has no replay id");let n=await h(r=>e.media?.replays?.stopCapture?.(o.replayId,r)).catch(r=>{throw new Error(`Overwolf replay stop failed: ${I(r)}`)});return O(n,"Overwolf replay stop failed"),n}useStreamingRecorder(){return this.videoCaptureBackend==="overwolf_recorder"&&this.videoUploadBackend==="chunked_hls"}useBrowserDisplayMedia(){return this.videoCaptureBackend==="browser_display_media"}desktopCaptureSettings(){return{enable:this.desktopCaptureOptions.enable,force_capture:this.desktopCaptureOptions.forceCapture,...this.desktopCaptureOptions.monitorId!==void 0?{monitor_id:this.desktopCaptureOptions.monitorId}:{}}}streamingSettings(e){let o=this.desktopCaptureOptions.enable?this.desktopCaptureSettings():{enable:!1,force_capture:!1};return{provider:e.streaming?.enums?.StreamingProvider?.VideoRecorder??"VideoRecorder",settings:{video:{fps:30,width:1920,height:1080,auto_calc_kbps:this.videoUploadBackend!=="chunked_hls",...this.videoUploadBackend==="chunked_hls"?{max_kbps:6e3,max_file_size_bytes:ze,include_full_size_video:!1,fragmented_video_file:!0}:{max_file_size_bytes:5e7,include_full_size_video:!0},override_overwolf_setting:!0,capture_desktop:o,game_window_capture:{enable_when_available:this.replayOptions.gameWindowCapture,capture_overwolf_windows:!1},keep_game_capture_on_lost_focus:!0,indication_position:"None",indication_type:"NoIndication"},audio:{mic:{enable:!1,volume:0},game:{enable:!0,volume:100}},peripherals:{capture_mouse_cursor:this.replayOptions.captureMouseCursor},quota:{max_quota_gb:this.replayOptions.quotaGb}}}}replaySettings(){return{settings:{video:{buffer_length:this.replayOptions.bufferLengthMs,...this.desktopCaptureOptions.enable?{capture_desktop:this.desktopCaptureSettings()}:{},game_window_capture:{enable_when_available:this.replayOptions.gameWindowCapture,capture_overwolf_windows:!1}},audio:{mic:{enable:!1,volume:0},game:{enable:!0,volume:100}},peripherals:{capture_mouse_cursor:this.replayOptions.captureMouseCursor},quota:{max_quota_gb:this.replayOptions.quotaGb}}}}async getRunningGame(e){let o=e.games?.getRunningGameInfo2??e.games?.getRunningGameInfo;if(o){let i=await h(d=>o.call(e.games,d)).catch(()=>null),s=P(i);if(s)return s}let n=e.games?.tracked?.getRunningGameInfo;if(n){let i=await h(d=>n.call(e.games?.tracked,d)).catch(()=>null),s=P(i);if(s)return s}let r=e.games?.tracked?.getAnyRunningGamesInfo;if(!r)return null;let a=await h(i=>r.call(e.games?.tracked,i)).catch(()=>null);return Ne(Pe(a))}async createSession(e){let o=await this.apiPost("/api/capture/sessions",{game:{overwolfGameId:e.overwolfGameId,rawOverwolfGameId:e.rawOverwolfGameId,name:e.name,telemetryAvailable:this.gepFeatures.length>0,features:this.gepFeatures,metadata:{source:"overwolf_native",processId:e.processId,processName:e.processName,windowTitle:e.windowTitle,commandLine:e.commandLine}},startedAt:new Date().toISOString(),sourceAppVersion:this.appVersion,metadata:{runtime:"overwolf_native",appId:this.appId,appVersion:this.appVersion,deviceName:this.deviceName,videoUploadBackends:this.videoUploadBackend==="chunked_hls"?["chunked_hls","overwolf_replay"]:["overwolf_replay"],supportedVideoUploadBackends:this.videoUploadBackend==="chunked_hls"?["chunked_hls","overwolf_replay"]:["overwolf_replay"],videoUploadBackend:this.videoUploadBackend,nativeCaptureBackend:this.useBrowserDisplayMedia()?"browser_display_media":this.useStreamingRecorder()?"overwolf_streaming":"overwolf_replay",videoFormat:this.videoUploadBackend==="chunked_hls"?"chunked_hls":"mp4",videoUploadMode:this.videoUploadBackend==="chunked_hls"?"chunked-hls":"single-file",inputCapture:{available:!!this.getOverwolf()?.games?.inputTracking,method:"overwolf_input_tracking",foregroundOnly:!0,storesPrintableText:!1},replay:{...this.replayOptions,captureBackend:this.videoCaptureBackend,desktopCapture:this.desktopCaptureOptions}}});if(!o.sessionId||!o.rootSegmentId)throw new Error("LookLoot capture session API returned an invalid response");return o}recordInput(e,o,n){if(!this.active)return;let r=p(e)?e:{},a=U({sessionId:this.active.serverSessionId,segmentId:this.active.rootSegmentId,atMs:this.relativeMs(),source:o==="mouse"?"mouse_action":"fallback_key",deviceType:o,state:n,keyCode:u(r.key??r.keyCode),virtualKey:u(r.key??r.keyCode),mouseButton:ge(r.button),wheelDelta:u(r.delta),movementDelta:u(r.x)!==void 0||u(r.y)!==void 0?{x:u(r.x),y:u(r.y)}:void 0,foregroundProcess:this.active.game.processName,foregroundProcessId:this.active.game.processId,captureMethod:"overwolf_input_tracking"});this.active.inputEvents.push(a)}async startNativeInputCapture(){!this.nativeInputSource?.readEvents||!this.active||(await this.nativeInputSource.start?.(),await this.pollNativeInputEvents(this.active),this.nativeInputPollTimer!==null&&clearInterval(this.nativeInputPollTimer),this.nativeInputPollTimer=setInterval(()=>{let e=this.active;e&&this.pollNativeInputEvents(e).catch(o=>this.emitError(o))},this.nativeInputPollIntervalMs))}async stopNativeInputCapture(e){this.nativeInputPollTimer!==null&&(clearInterval(this.nativeInputPollTimer),this.nativeInputPollTimer=null),this.nativeInputSource?.readEvents&&(await this.pollNativeInputEvents(e),await this.nativeInputSource.stop?.())}async pollNativeInputEvents(e){let o=await this.nativeInputSource?.readEvents?.();if(!(!Array.isArray(o)||o.length===0))for(let n of o)this.recordNativeInput(e,n)}recordNativeInput(e,o){let n=p(o)?o:{},r=lt(n.DeviceType??n.deviceType);if(!Lt(n,e.game)){this.recordNativeFocusReset(e,n,r);return}let a=U({sessionId:e.serverSessionId,segmentId:e.rootSegmentId,atMs:Math.max(0,u(n.RelativeMs??n.relativeMs)??Date.now()-e.startedWallMs),source:r==="mouse"?"mouse_action":"fallback_key",deviceType:r,state:ut(n.State??n.state),keyCode:u(n.KeyCode??n.keyCode),virtualKey:u(n.KeyCode??n.keyCode),mouseButton:ge(n.Button??n.button),wheelDelta:u(n.WheelDelta??n.wheelDelta),movementDelta:u(n.X??n.x)!==void 0||u(n.Y??n.y)!==void 0?{x:u(n.X??n.x),y:u(n.Y??n.y)}:void 0,foregroundProcess:l(n.ForegroundProcessName??n.foregroundProcessName)??e.game.processName,foregroundProcessId:u(n.ForegroundProcessId??n.foregroundProcessId)??e.game.processId,foregroundTitle:l(n.ForegroundWindowTitle??n.foregroundWindowTitle),captureMethod:"native_helper",nativeTimestampMs:u(n.AtUnixMs??n.atUnixMs)});e.inputEvents.push(a),St(e,a)}recordNativeFocusReset(e,o,n){if(e.nativeHeldInputKeys.size===0)return;let r=U({sessionId:e.serverSessionId,segmentId:e.rootSegmentId,atMs:Math.max(0,u(o.RelativeMs??o.relativeMs)??Date.now()-e.startedWallMs),source:"fallback_key",deviceType:n,state:"released",action:"focus_lost_clear",foregroundProcess:l(o.ForegroundProcessName??o.foregroundProcessName),foregroundProcessId:u(o.ForegroundProcessId??o.foregroundProcessId),foregroundTitle:l(o.ForegroundWindowTitle??o.foregroundWindowTitle),captureMethod:"native_helper",nativeTimestampMs:u(o.AtUnixMs??o.atUnixMs)});e.inputEvents.push(r),e.nativeHeldInputKeys.clear()}recordGep(e,o){if(!this.active)return;let n=p(e)?e:{},r=this.active.game.overwolfGameId??this.active.game.rawOverwolfGameId??0,a=Array.isArray(n.events)?n.events:Array.isArray(n.data)?n.data:[e];for(let i of a){let s=p(i)?i:{};this.active.gameStateEvents.push(le({sessionId:this.active.serverSessionId,segmentId:this.active.rootSegmentId,atMs:this.relativeMs(),source:o,gameId:r,feature:l(s.feature??n.feature),category:l(s.category??n.category),key:l(s.key??s.name),name:l(s.name??s.key),raw:i}))}}async captureGepSnapshot(){let e=this.getOverwolf()?.games?.events;if(!this.active||!e?.getInfo)return;let o=await h(n=>e.getInfo?.(n)).catch(()=>null);o&&this.recordGep(o,"overwolf_gep_snapshot")}relativeMs(){return this.active?Math.max(0,Date.now()-this.active.startedWallMs):0}async handleVideoFileSplit(e){let o=this.active;if(!o?.chunkedVideo||o.streamId===void 0)return;let n=X(e);if(n!==void 0&&n!==o.streamId)return;let r=_t(e);if(!r)return;let a=u(e.count),i=a!==void 0&&a>0?Math.max(0,Math.round(a)-1):o.chunkedVideo.uploadedSegments.length,s=M(u(e.duration),V)??V;this.enqueueNativeChunkUpload(o,{filePath:r,index:i,durationMs:s,final:!1})}async handleStreamingSourceImageChanged(e){let o=this.active;if(!o||o.videoBackend!=="overwolf_streaming")return;let n=X(e);if(n!==void 0&&n!==o.streamId)return;let r=l(e.new_source??e.newSource??e.source)?.toLowerCase();if(!r||!r.includes("desktop")||this.desktopCaptureOptions.enable)return;let a="Overwolf Native switched to desktop capture. LookLoot stopped the capture because desktop fallback is disabled.";throw o.chunkedVideo&&(o.chunkedVideo.status="failed",o.chunkedVideo.lastError=a),o.abortReason=a,this.emitUploadProgress({status:"failed",reason:a}),await this.stop().catch(()=>{}),new Error(a)}enqueueNativeChunkUpload(e,o){let n=e.chunkedVideo;if(!n)return;let r=Nt(o.filePath);n.uploadedPaths.has(r)||(n.uploadedPaths.add(r),n.status=o.final?"uploading":n.status,this.emitUploadProgress({status:"uploading"}),n.chain=n.chain.then(()=>this.uploadNativeVideoChunk(e,o)).catch(a=>{n.status="failed",n.lastError=I(a),this.emitUploadProgress({status:"failed",reason:n.lastError}),this.emitError(a)}))}enqueueNativeBlobChunkUpload(e,o){let n=e.chunkedVideo;if(!n)return;let r=`browser-display-media:${o.index}:${o.blob.size}`;n.uploadedPaths.has(r)||(n.uploadedPaths.add(r),this.emitUploadProgress({status:"uploading"}),n.chain=n.chain.then(()=>this.uploadNativeVideoBlobChunk(e,o)).catch(a=>{n.status="failed",n.lastError=I(a),this.emitUploadProgress({status:"failed",reason:n.lastError}),this.emitError(a)}))}async uploadNativeVideoChunk(e,o){let n=e.chunkedVideo,r=this.uploadBridge??D();if(!n||!r?.uploadFile)return;let a=ke(n,o.index),i=`segment-${String(a).padStart(6,"0")}.mp4`,s=`video/${i}`,[d]=await this.presignBatch(e.serverSessionId,"video",[{path:s,contentType:"video/mp4"}]);if(!d)throw new Error(`No signed upload object returned for ${s}`);let c=await r.uploadFile({filePath:o.filePath,uploadUrl:d.uploadUrl,headers:d.headers}),m=n.uploadedSegments[n.uploadedSegments.length-1]?.endMs??0,w=Math.max(1,Math.round(o.durationMs??V)),y={index:a,uri:i,contentType:"video/mp4",filePath:o.filePath,objectKey:d.objectKey,publicUrl:d.publicUrl,sizeBytes:c.sizeBytes,durationMs:w,startMs:m,endMs:m+w};n.uploadedSegments.push(y),n.uploadedSegments.sort((g,f)=>g.index-f.index),await this.uploadNativePlaylist(e,o.final)}async uploadNativeVideoBlobChunk(e,o){let n=e.chunkedVideo;if(!n)return;let r=ke(n,o.index),a=`segment-${String(r).padStart(6,"0")}.webm`,i=`video/${a}`,s=e.browserDisplayMedia?.mimeType??o.blob.type??Me,[d]=await this.presignBatch(e.serverSessionId,"video",[{path:i,contentType:s,sizeBytes:o.blob.size}]);if(!d)throw new Error(`No signed upload object returned for ${i}`);let c=await this.putBlob(d.uploadUrl,d.headers,o.blob),m=n.uploadedSegments[n.uploadedSegments.length-1]?.endMs??0,w=Math.max(1,Math.round(o.durationMs)),y={index:r,uri:a,contentType:s,filePath:`browser-display-media:${r}`,objectKey:d.objectKey,publicUrl:d.publicUrl,sizeBytes:c.sizeBytes,durationMs:w,startMs:m,endMs:m+w};n.uploadedSegments.push(y),n.uploadedSegments.sort((g,f)=>g.index-f.index),await this.uploadNativePlaylist(e,!1)}async uploadNativePlaylist(e,o){let n=e.chunkedVideo;if(!n||n.uploadedSegments.length===0)return;let[r]=await this.presignBatch(e.serverSessionId,"video",[{path:"video/index.m3u8",contentType:"application/vnd.apple.mpegurl"}]);if(!r)throw new Error("No signed upload object returned for video/index.m3u8");let a=Ct(n,{final:o});await this.putText(r.uploadUrl,r.headers,a),n.uploadedPlaylistKey=r.objectKey,n.uploadedPlaylistUrl=r.publicUrl,n.status=o?"ready":"recording",await this.checkpointNativeChunkedVideo(e,o),this.emitUploadProgress({status:o?"ready":"uploading"})}async uploadFinalNativeChunk(e,o){let n=e.chunkedVideo;if(!n)return;if(e.videoBackend==="browser_display_media"){await n.chain;return}let r=K(o);r&&this.enqueueNativeChunkUpload(e,{filePath:r,index:n.uploadedSegments.length,durationMs:Math.max(1,Date.now()-e.startedWallMs-Ge(n)),final:!0}),await n.chain}async finishNativeChunkedVideo(e){let o=e.chunkedVideo;o&&(await o.chain,o.uploadedSegments.length>0?await this.uploadNativePlaylist(e,!0):(o.status="failed",o.lastError=e.videoBackend==="browser_display_media"?"Browser window capture did not produce any video chunks.":"Overwolf Native did not produce any split video chunks.",this.emitUploadProgress({status:"failed",reason:o.lastError})))}async checkpointNativeChunkedVideo(e,o){let n=e.chunkedVideo;if(!n?.uploadedPlaylistKey)return;let r=G(n,o?Math.max(0,Date.now()-e.startedWallMs):null);await this.apiPost(`/api/capture/sessions/${e.serverSessionId}/checkpoint`,{uploadedShardKeys:be(n),artifactKeys:{canonicalVideoPlaylistKey:n.uploadedPlaylistKey},canonicalVideo:r})}emitUploadProgress(e){let o=this.active,n=o?.chunkedVideo??null;this.emit("upload-progress",{status:e.status??(n?.status==="ready"?"ready":"idle"),videoUploadBackend:o?.videoUploadBackend??this.videoUploadBackend,playbackUrl:e.playbackUrl??n?.uploadedPlaylistUrl??null,uploadedPlaylistKey:e.uploadedPlaylistKey??n?.uploadedPlaylistKey??null,uploadedPlaylistUrl:e.uploadedPlaylistUrl??n?.uploadedPlaylistUrl??null,uploadedSegmentCount:e.uploadedSegmentCount??n?.uploadedSegments.length??0,reason:e.reason??n?.lastError??void 0})}async uploadFinalizedSession(e){let o=e.videoUploadBackend==="chunked_hls"?null:await this.uploadVideo(e),n=ve(e.inputEvents),r=ve(e.gameStateEvents),a=await this.uploadText(e.serverSessionId,"inputs","inputs.ndjson","application/x-ndjson",n),i=await this.uploadText(e.serverSessionId,"game_state","game_state.ndjson","application/x-ndjson",r),s=this.createManifest(e,{video:o,inputs:a,gameState:i}),d=JSON.stringify(s,null,2),c=await this.uploadText(e.serverSessionId,"manifest","manifest.json","application/json",d);await this.apiPost(`/api/capture/sessions/${e.serverSessionId}/complete`,{endedAt:e.endedAt,status:"uploaded",uploads:[o,a,i,c].filter(Pt).map(m=>({objectId:m.objectId,status:"uploaded",sizeBytes:m.sizeBytes,checksumSha256:m.checksumSha256})),metadata:{runtime:"overwolf_native",appId:this.appId,appVersion:this.appVersion,durationMs:e.durationMs,inputsCount:e.inputEvents.length,gameStateEventCount:e.gameStateEvents.length,videoFormat:e.videoUploadBackend==="chunked_hls"?"chunked_hls":"mp4",videoUploadBackend:e.videoUploadBackend,nativeCaptureBackend:e.replay.backend,videoUploadMode:e.videoUploadBackend==="chunked_hls"?"chunked-hls":"single-file",replay:e.replay,...o?{singleFileVideo:ye(o,e.replay.backend),video:ye(o,e.replay.backend)}:{canonicalVideo:G(e.chunkedVideo,e.durationMs),video:{provider:"chunked_hls",canonicalVideo:G(e.chunkedVideo,e.durationMs)}},captureUpload:{uploadedShardKeys:[...be(e.chunkedVideo),a.objectKey,i.objectKey,c.objectKey],artifactKeys:{...o?{videoKey:o.objectKey}:{},canonicalVideoPlaylistKey:e.chunkedVideo?.uploadedPlaylistKey??null,inputsKey:a.objectKey,gameStateKey:i.objectKey,manifestKey:c.objectKey}}}}),await this.removeQueuedSession(e.id)}createManifest(e,o){let n={overwolfGameId:e.game.overwolfGameId??e.game.rawOverwolfGameId??0,name:e.game.name,features:this.gepFeatures,telemetryAvailable:this.gepFeatures.length>0};return ue({sessionId:e.serverSessionId,game:n,startedAt:e.startedAt,endedAt:e.endedAt,clock:{wallStartedAt:e.startedAt,monotonicStartedAtMs:0},segments:[{id:e.rootSegmentId,kind:"session",label:e.game.name,startedAt:e.startedAt,endedAt:e.endedAt,startedAtMs:0,endedAtMs:e.durationMs,ordinal:0}],uploads:[...o.video?[{kind:"video",objectKey:o.video.objectKey,contentType:"video/mp4",sizeBytes:o.video.sizeBytes,checksumSha256:o.video.checksumSha256}]:Et(e.chunkedVideo),{kind:"inputs",objectKey:o.inputs.objectKey,contentType:"application/x-ndjson",sizeBytes:o.inputs.sizeBytes},{kind:"game_state",objectKey:o.gameState.objectKey,contentType:"application/x-ndjson",sizeBytes:o.gameState.sizeBytes}],metadata:{runtime:"overwolf_native",videoUploadBackend:e.videoUploadBackend,videoUploadMode:e.videoUploadBackend==="chunked_hls"?"chunked-hls":"single-file",nativeCaptureBackend:e.replay.backend,canonicalVideo:G(e.chunkedVideo,e.durationMs),replay:e.replay}})}async uploadVideo(e){let o=this.uploadBridge??D();if(!o?.uploadFile)throw new Error("LookLoot Overwolf Native upload bridge is not registered");let n=await this.presign(e.serverSessionId,"video","overwolf-replay.mp4","video/mp4");try{let r=await o.uploadFile({filePath:e.replay.mediaPath,uploadUrl:n.uploadUrl,headers:n.headers});return await this.completeUpload(n.objectId,{status:"uploaded",sizeBytes:r.sizeBytes,checksumSha256:r.checksumSha256}),{objectId:n.objectId,objectKey:n.objectKey,status:"uploaded",sizeBytes:r.sizeBytes,checksumSha256:r.checksumSha256}}catch(r){throw await this.completeUpload(n.objectId,{status:"failed"}).catch(()=>{}),r}}async uploadText(e,o,n,r,a){let i=await this.presign(e,o,n,r,new Blob([a]).size),s=new Blob([a],{type:r}),d=await fetch(i.uploadUrl,{method:"PUT",headers:i.headers,body:s});if(!d.ok)throw await this.completeUpload(i.objectId,{status:"failed"}).catch(()=>{}),new Error(`Upload failed: ${d.status}`);return await this.completeUpload(i.objectId,{status:"uploaded",sizeBytes:s.size}),{objectId:i.objectId,objectKey:i.objectKey,status:"uploaded",sizeBytes:s.size}}presign(e,o,n,r,a){return this.apiPost("/api/capture/uploads/presign",{sessionId:e,kind:o,fileName:n,contentType:r,sizeBytes:a})}async presignBatch(e,o,n){return(await this.apiPost("/api/capture/uploads/batch-presign",{sessionId:e,kind:o,files:n})).objects??[]}async putText(e,o,n){let r=await fetch(e,{method:"PUT",headers:o,body:new Blob([n],{type:o["content-type"]??"text/plain"})});if(!r.ok)throw new Error(`Upload failed: ${r.status}`)}async putBlob(e,o,n){let r=await fetch(e,{method:"PUT",headers:o,body:n});if(!r.ok)throw new Error(`Upload failed: ${r.status}`);return{sizeBytes:n.size}}completeUpload(e,o){return this.apiPost(`/api/capture/uploads/${e}/complete`,o)}async apiPost(e,o){if(!this.token)throw new Error("LookLoot device token is not configured");let n=await fetch(`${this.apiBaseUrl}${e}`,{method:"POST",headers:{authorization:`Bearer ${this.token}`,"content-type":"application/json"},body:JSON.stringify(o)}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(typeof r.error=="string"?r.error:`LookLoot API failed: ${n.status}`);return r}async queueSession(e,o){let n=Q().filter(r=>r.id!==e.id);n.push({...e,attempts:0,lastError:o}),A(n)}async removeQueuedSession(e){A(Q().filter(o=>o.id!==e))}setState(e){this.state!==e&&(this.state=e,this.emit("state-change",e))}emitError(e){let o=e instanceof Error?e:new Error(String(e));this.emit("error",o)}localVerificationBase(e,o,n){return{ok:!1,reason:n,durationMs:e,diagnostics:o,video:{ok:!1,reason:n??"Verification did not run."},input:{ok:!1,eventCount:0,reason:n??"Verification did not run."},gameState:{ok:!1,eventCount:0,snapshotCount:0,required:!1,features:[...this.gepFeatures]},upload:we()}}},je="lookloot-process-presence",$e=new Set(["applicationframehost.exe","chrome.exe","cmd.exe","code.exe","codex.exe","cursor.exe","discord.exe","electron.exe","epicgameslauncher.exe","explorer.exe","firefox.exe","gamebar.exe","gamebarftserver.exe","githubdesktop.exe","launcher.exe","lookloot.exe","msedge.exe","node.exe","notepad.exe","overwolf.exe","overwolfbrowser.exe","powershell.exe","riotclientservices.exe","screenclippinghost.exe","slack.exe","steam.exe","steamwebhelper.exe","systemsettings.exe","taskmgr.exe","teams.exe","textinputhost.exe","windowsterminal.exe"]),We=["/google/chrome/","/microsoft/edge/","/microsoft vs code/","/nodejs/","/windows/systemapps/","/windows/system32/","/windows/syswow64/"],Oe=new Set(["program manager","settings","task manager","textinputhost","windows input experience","xbox game bar"]);function Kt(t){let e=t.input?.nativeSource??qe({nativeProcess:t.nativeProcess}),o={...t.replay,captureBackend:t.replay?.captureBackend??"browser_display_media"},n={...t.upload,videoBackend:t.upload?.videoBackend??"chunked_hls"},r=new ee({...t,replay:o,upload:n,input:{...t.input,nativeSource:e}}),a=o.captureBackend==="browser_display_media";return{capture:r,init:()=>r.init(),async start(i={}){let s=b(i.game)??await pe({nativeProcess:t.nativeProcess}),d=Ue(i.browserDisplayMedia),c=a&&!d?await me({...t.gameWindowCapture,...i.gameWindowCapture,game:s}):d;return r.start({game:s??i.game,browserDisplayMedia:c??void 0})},stop:()=>r.stop(),dispose:()=>r.dispose(),getState:()=>r.getState(),getLiveInputEvents:i=>r.getLiveInputEvents(i),getRunningGame:()=>pe({nativeProcess:t.nativeProcess}),requestGameWindowSource:(i={})=>me({...t.gameWindowCapture,...i}),getRuntimeDiagnostics:()=>r.getRuntimeDiagnostics(),runLocalVerification:i=>r.runLocalVerification(i),openLocalVerificationViewer:i=>r.openLocalVerificationViewer(i),retryFailedUpload:()=>r.retryFailedUpload(),discardCaptureUpload:()=>r.discardCaptureUpload()}}async function pe(t={}){let e=await Je(t.nativeProcess);if(e)return e;let n=Ee()?.games,r=n?.getRunningGameInfo2??n?.getRunningGameInfo;if(r){let d=await h(m=>r.call(n,m)).catch(()=>null),c=P(d);if(c)return H(c)}let a=n?.tracked?.getRunningGameInfo;if(a){let d=await h(m=>a.call(n?.tracked,m)).catch(()=>null),c=P(d);if(c)return H(c)}let i=n?.tracked?.getAnyRunningGamesInfo;if(!i)return null;let s=await h(d=>i.call(n?.tracked,d)).catch(()=>null);return H(Ne(Pe(s)))}async function me(t={}){let e=b(t.game),o=globalThis.navigator?.mediaDevices;if(!o?.getUserMedia&&!o?.getDisplayMedia)throw new Error("Browser game-window capture is not available in this Overwolf runtime.");if(t.preferDirectWindowHandle!==!1&&e?.windowHandle&&o.getUserMedia){let a=await Ye(o,e,t).catch(()=>null);if(a)return a}if(!o.getDisplayMedia)throw new Error("Choose a game window in a runtime that supports browser display capture.");let n=_e(t),r=await o.getDisplayMedia({video:{displaySurface:"window",frameRate:{ideal:M(t.frameRate,30)},width:{ideal:n.width},height:{ideal:n.height},cursor:t.cursor??"always"},audio:!1,monitorTypeSurfaces:"exclude",selfBrowserSurface:"exclude",surfaceSwitching:"exclude"});try{return x(r,e)}catch(a){throw _({stream:r}),a}}async function F(t={}){let e=await Xe(t.plugin);if(e)return e;let o=Ee()?.extensions?.current;if(!o?.getExtraObject)return null;let n=t.extraObjectId??je,r=await h(i=>o.getExtraObject?.(n,i)).catch(()=>null);if(!r||p(r)&&(r.status==="error"||r.success===!1))return null;let a=p(r)&&p(r.object)?r.object:r;return p(a)?a:null}function qe(t={}){return{async start(){let e=await F(t.nativeProcess),o=e?.StartInputCaptureJson??e?.startInputCaptureJson;z(e,o)},async stop(){let e=await F(t.nativeProcess),o=e?.StopInputCaptureJson??e?.stopInputCaptureJson;z(e,o)},async readEvents(){let e=await F(t.nativeProcess),o=e?.ReadInputEventsJson??e?.readInputEventsJson,n=z(e,o),a=(oe(n?.events,n?.inputEvents,n?.data)??[]).filter(p);return a.length>0&&t.onEvents?.(a),a}}}function He(t,e={}){let o=[];for(let n of t){let r=W(n.Name??n.name);if(!r)continue;let a=l(n.ExecutablePath??n.executablePath??n.path),i=l(n.CommandLine??n.commandLine),s=Qe(l(n.MainWindowTitle??n.mainWindowTitle??n.windowTitle),e),d=$(a),c=$(i),m=Ze(r,e.gameCatalog);if(!m&&et(r,d,s,e)||!m&&tt(r,d,c,s))continue;let w={processId:u(n.ProcessId??n.processId),processName:l(n.Name??n.name)??r,processPath:a,commandLine:i,windowTitle:s,windowHandle:u(n.MainWindowHandle??n.mainWindowHandle??n.windowHandle),isForeground:Re(n.IsForeground??n.isForeground)??!1,createdAt:Date.parse(l(n.CreationDate??n.creationDate)??"")||0};if(m){o.push(J(w,{name:m.displayName,iconUrl:m.iconUrl,detectedBy:"native_process_catalog",detectionConfidence:"high",priority:8+(w.isForeground?3:0)+(s?1:0)}));continue}let y=ot({processName:r,processPath:a,windowTitle:s},e.installedGames??[]);if(y){o.push(J(w,{name:l(y.displayName??y.name)??s??r,iconUrl:l(y.iconUrl),detectedBy:"native_process_installed_game",detectionConfidence:"medium",priority:5+(w.isForeground?3:0)+(s?1:0)}));continue}let g=nt(a);g&&o.push(J(w,{name:rt(g,e.installedGames??[])??g,detectedBy:"native_process_install_path",detectionConfidence:"medium",priority:3+(w.isForeground?3:0)+(s?1:0)}))}return at(o).sort((n,r)=>+!!r.isForeground-+!!n.isForeground||j(r)-j(n)||r.createdAt-n.createdAt).map(({priority:n,createdAt:r,...a})=>a)}async function Je(t={}){let e=await F(t),o=e?.GetProcessListJson??e?.getProcessListJson,n=z(e,o),r=oe(n?.processes,n?.data,n?.results,n?.res)??[];return He(r.filter(p),t)[0]??null}function H(t){return t?{overwolfGameId:t.overwolfGameId,rawOverwolfGameId:t.rawOverwolfGameId,name:t.name,processId:t.processId,processName:t.processName,processPath:t.processPath,windowHandle:t.windowHandle,windowTitle:t.windowTitle,commandLine:t.commandLine,iconUrl:t.iconUrl,isForeground:t.isForeground,detectedBy:t.detectedBy,detectionConfidence:t.detectionConfidence}:null}async function Ye(t,e,o){let n=e.windowHandle;if(!n)throw new Error("No game window handle is available for direct native capture.");let r=_e(o);if(!t.getUserMedia)throw new Error("Direct native game-window capture is not available in this Overwolf runtime.");let a=await t.getUserMedia({audio:!1,video:{mandatory:{chromeMediaSource:"desktop",chromeMediaSourceId:`window:${Math.round(n)}:0`,maxFrameRate:M(o.frameRate,30),maxWidth:r.width,maxHeight:r.height}}});try{let[i]=a.getVideoTracks();if(!i||i.readyState!=="live")throw new Error(`Direct native game-window capture ended before recording started (${i?.readyState??"missing"}).`);if("contentHint"in i)try{i.contentHint="motion"}catch{}let s=i.getSettings(),d=typeof s.deviceId=="string"?s.deviceId.toLowerCase():"";if(d&&!d.startsWith(`window:${Math.round(n)}:`))throw new Error("Direct native capture did not return the requested game window.");return x(a,e,{label:e.name?`${e.name} game window`:i.label,displaySurface:"window"})}catch(i){throw _({stream:a}),i}}function _e(t){let e=typeof globalThis.window<"u"&&Number.isFinite(globalThis.window.devicePixelRatio)?Math.max(1,globalThis.window.devicePixelRatio):1,o=typeof globalThis.window<"u"&&globalThis.window.screen?.width?Math.round(globalThis.window.screen.width*e):1920,n=typeof globalThis.window<"u"&&globalThis.window.screen?.height?Math.round(globalThis.window.screen.height*e):1080;return{width:Math.min(3840,Math.max(1280,Math.round(M(t.width,o)))),height:Math.min(2160,Math.max(720,Math.round(M(t.height,n))))}}async function Xe(t){return t?typeof t=="function"?await t():t:null}function z(t,e,...o){if(!t||!e)return null;try{let n=it(e.apply(t,o));return!n||n.ok===!1||n.success===!1?null:n}catch{return null}}function W(t){let e=l(t)?.toLowerCase();return e?e.endsWith(".exe")?e:`${e}.exe`:""}function Qe(t,e){let o=t?.replace(/[\u0000\u200B-\u200F\uFEFF]/g,"").trim();return o?new Set([...Oe,...(e.nonGameWindowTitles??[]).map(r=>r.trim().toLowerCase())]).has(o.toLowerCase())?void 0:o:void 0}function Ze(t,e=[]){return e.find(o=>[...o.processNames??[],...o.executableNames??[]].some(r=>W(r)===t))}function et(t,e,o,n){if(new Set([...$e,...(n.nonGameProcessNames??[]).map(W)]).has(t)||[...We,...n.nonGamePathHints??[]].map(s=>s.replaceAll("\\","/").toLowerCase()).some(s=>e.includes(s)))return!0;let i=o?.trim().toLowerCase();return!!(i&&Oe.has(i))}function tt(t,e,o,n){let r=N(n);return/installer|setup|bootstrap|crash|reporter|service|updater|patcher/.test(t)||r&&/installer|setup|launcher|updater|patcher|bootstrapper/.test(r)?!0:/\/(?:installers?|launchers?|setup|updater|patcher)\//.test(`${e} ${o}`)}function J(t,e){return{...t,name:e.name,iconUrl:e.iconUrl,detectedBy:e.detectedBy,detectionConfidence:e.detectionConfidence,priority:e.priority}}function ot(t,e){let o=t.processName.toLowerCase(),n=$(t.processPath),r=N(t.windowTitle);return e.find(a=>{let i=W(a.processName);if(i&&i===o)return!0;let s=$(l(a.installPath));if(s&&n.startsWith(s))return!0;let d=N(l(a.displayName??a.name));return!!(r&&d&&r.includes(d))})}function nt(t){if(!t)return null;let e=Ce(t),o=e.match(/\/steamapps\/common\/([^/]+)/i)?.[1];if(o&&o.toLowerCase()!=="wallpaper engine")return fe(o);let n=[/\/epic games\/([^/]+)/i,/\/gog galaxy\/games\/([^/]+)/i,/\/xboxgames\/([^/]+)/i,/\/ea games\/([^/]+)/i,/\/ubisoft\/(?:ubisoft game launcher\/)?games\/([^/]+)/i,/\/battle\.net\/([^/]+)/i,/\/games\/([^/]+)/i];for(let r of n){let a=e.match(r);if(a?.[1])return fe(a[1])}return null}function rt(t,e){let o=N(t),n=e.find(r=>{let a=N(l(r.displayName??r.name));return!!(a&&(a===o||o.includes(a)))});return l(n?.displayName??n?.name)}function at(t){let e=new Map;for(let o of t){let n=N(o.name),r=e.get(n);(!r||j(o)>j(r))&&e.set(n,o)}return[...e.values()]}function j(t){let e=t.priority;return t.windowHandle&&(e+=3),t.windowTitle&&(e+=2),t.isForeground&&(e+=3),/win64-shipping|shipping|game\.exe$|client.*win64/i.test(t.processName??"")&&(e+=5),/launcher|bootstrap|installer|setup|patcher|updater/i.test(t.processName??"")&&(e-=5),e}function Ce(t){return(t??"").replaceAll("\\","/").replace(/\/+/g,"/").replace(/^([A-Za-z]):\//,"$1:/")}function $(t){return Ce(t).toLowerCase()}function N(t){return t?.toLowerCase().replace(/\.exe$/i,"").replace(/[^a-z0-9]+/g," ").replace(/\s+/g," ").trim()??""}function fe(t){return t.replace(/[_-]+/g," ").replace(/\s+/g," ").trim().replace(/\b\w/g,e=>e.toUpperCase())}function Ee(){let t=globalThis;return t.window?.overwolf??t.overwolf??null}function S(t,e,o){return t?{ok:!0,required:e}:{ok:!e,required:e,reason:o}}function h(t){return new Promise((e,o)=>{try{t(n=>e(n))}catch(n){o(n)}})}function O(t,e){if(!t)throw new Error(e);if(t.success===!1||t.status==="error")throw new Error(l(t.error??t.reason)??e)}function b(t){let e=p(t)?t:{},o=Te(e.gameInfo,e.game,e.runningGameInfo,e.res,e),n=u(o.rawOverwolfGameId??o.id??o.gameId??o.classId),r=u(o.overwolfGameId??o.gameId??o.classId??n),a=l(o.name??o.displayName??o.gameName??o.title??o.shortTitle??e.name??e.title);if(!a&&r===void 0)return null;let i=l(o.processPath??o.executionPath??o.path),s=l(o.processName)??Be(i)??l(o.executionPath);return{overwolfGameId:r,rawOverwolfGameId:n??r,name:a??`Overwolf game ${r}`,processId:u(o.processId??o.pid),processName:s,processPath:i,windowHandle:u(o.windowHandle??o.mainWindowHandle),windowTitle:l(o.windowTitle??o.title??e.windowTitle),commandLine:l(o.commandLine),iconUrl:l(o.iconUrl??o.icon??o.imageUrl),isForeground:Re(o.isForeground??e.isForeground),detectedBy:l(o.detectedBy),detectionConfidence:dt(o.detectionConfidence)}}function P(t){return!t||xe(t)===!1?null:b(t)}function Pe(t){if(!t)return[];let e=Array.isArray(t)?t:p(t)?oe(t.games,t.gameInfos,t.runningGames,t.runningGamesInfo,t.data,t.res,t.results)??st(t.games,t.gameInfos,t.runningGames,t.runningGamesInfo,t.data)??[t]:[],o=[];for(let n of e){let r=P(n);r&&o.push(r)}return o}function Ne(t){return t.find(e=>e.processName?.toLowerCase()==="robloxplayerbeta.exe")??t.find(e=>e.processName?.toLowerCase()==="deadlock.exe")??t.find(e=>/valorant/i.test(e.processName??e.name))??t[0]??null}function xe(t){let e=p(t)?t:{},o=Te(e.gameInfo,e.game,e.runningGameInfo,e),n=p(o)?o.isRunning??o.running??e.isRunning??e.running:void 0;return typeof n=="boolean"?n:null}function Y(t,e){return e?t.overwolfGameId!==void 0&&e.overwolfGameId!==void 0?t.overwolfGameId===e.overwolfGameId:t.name.toLowerCase()===e.name.toLowerCase():!0}function Te(...t){return t.find(p)??{}}function oe(...t){return t.find(Array.isArray)}function it(t){if(p(t))return t;if(typeof t!="string")return null;try{let e=JSON.parse(t);return p(e)?e:null}catch{return null}}function st(...t){for(let e of t){if(!p(e)||Array.isArray(e))continue;let o=Object.values(e).filter(p);if(o.length)return o}}function u(t){let e=typeof t=="number"?t:Number(t);return Number.isFinite(e)?e:void 0}function Re(t){if(t===!0||t==="true"||t===1||t==="1")return!0;if(t===!1||t==="false"||t===0||t==="0")return!1}function dt(t){return t==="low"||t==="medium"||t==="high"?t:void 0}function l(t){return typeof t=="string"&&t.trim()?t.trim():void 0}function Be(t){if(t)return t.replaceAll("\\","/").split("/").pop()||void 0}function ge(t){if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t!="string")return;let e=t.toLowerCase();return e==="left"||e==="mouse_left"?1:e==="right"||e==="mouse_right"?2:e==="middle"||e==="mouse_middle"?3:e==="xbutton1"||e==="back"?4:e==="xbutton2"||e==="forward"?5:u(t)}function lt(t){return String(t??"").toLowerCase()==="mouse"?"mouse":"keyboard"}function ut(t){let e=String(t??"").toLowerCase();return e==="released"||e==="up"?"released":e==="repeat"||e==="wheel"||e==="move"?"repeat":"pressed"}function ct(t){let e=l(t.replayId??t.id);if(e)return e;let n=(te(t)??K(t))?.split(/[\\/]/).pop()?.replace(/\.[^.]+$/,"");return n?.split(/[-_\s]/).pop()??n}function X(t){return u(t.stream_id??t.streamId)}function K(t){return l(t.media_path??t.path??t.file_path??t.last_file_path)??pt(t.media_path_encoded)}function te(t){return l(t.media_url??t.url??t.media_path_encoded)}function pt(t){let e=l(t);if(!(!e||!/^file:\/\//i.test(e)))try{return decodeURIComponent(new URL(e).pathname).replace(/^\/([A-Za-z]:\/)/,"$1")}catch{return}}function he(t){let e=Number(t);return Number.isFinite(e)?Math.max(0,Math.min(6e4,Math.round(e))):8e3}function mt(t){return new Promise(e=>setTimeout(e,t))}function ft(t){return{overwolfGameId:t.overwolfGameId,rawOverwolfGameId:t.rawOverwolfGameId,name:t.name,processId:t.processId,processName:t.processName,commandLine:t.commandLine}}function we(){return{attempted:!1,reason:"Local verification is Overwolf-only and does not mint a LookLoot token, create a remote session, or upload. Use init/start/stop with a real device token and the partner capture verification API to verify remote ingestion."}}function gt(t,e){return[t.checks.overwolf,e.hasExplicitGame?null:t.checks.games,t.checks.replays,e.requireInput?t.checks.inputTracking:null,e.requireGep?t.checks.gep:null].filter(n=>!!n).filter(n=>!n.ok).map(n=>n.reason??"Runtime diagnostics failed.")}function M(t,e){let o=Number(t);return Number.isFinite(o)&&o>0?o:e}function ht(t){return t===!0?{enable:!0,forceCapture:!0}:p(t)?{enable:t.enable!==!1,forceCapture:t.forceCapture!==!1,monitorId:u(t.monitorId)}:{enable:!1,forceCapture:!1}}function wt(t){return Array.from(new Set(t.map(e=>e.trim()).filter(Boolean)))}function E(t){return t.toLowerCase().replace(/\.exe\b/g,"").replace(/[^a-z0-9]+/g,"").trim()}function p(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function ve(t){return t.map(e=>JSON.stringify(e)).join(`
2
+ `)+(t.length?`
3
+ `:"")}function ye(t,e="overwolf_replay"){return{provider:e,status:"ready",objectKey:t.objectKey,contentType:"video/mp4",sizeBytes:t.sizeBytes??null,checksumSha256:t.checksumSha256??null}}function vt(t="overwolf_native_split"){return{provider:"chunked_hls",status:"recording",source:t,segmentSeconds:t==="browser_display_media"?Ie/1e3:V/1e3,uploadedSegments:[],uploadedPaths:new Set,uploadedPlaylistKey:null,uploadedPlaylistUrl:null,chain:Promise.resolve(),lastError:null}}function yt(t){let e=()=>{},o=()=>{},n=new Promise((r,a)=>{e=r,o=a});return{...t,recorder:null,mimeType:kt(),stopped:n,resolveStopped:e,rejectStopped:o,lastSegmentWallMs:Date.now(),nextSegmentIndex:0}}function Ue(t){return t?typeof MediaStream<"u"&&t instanceof MediaStream?x(t,null):typeof MediaStream>"u"||!p(t)||!(t.stream instanceof MediaStream)?null:x(t.stream,null,{label:l(t.label)??void 0,displaySurface:l(t.displaySurface)??void 0}):null}function x(t,e,o={}){let[n]=t.getVideoTracks();if(!n)throw new Error("Choose the game window to start capture.");let r=n.getSettings(),a=o.displaySurface??r.displaySurface,i=o.label??n.label;if(a==="monitor"||a==="browser"||Mt(i))throw new Error("Choose the game window, not the full screen or a browser tab.");if(e&&i&&!Ot(i)&&!bt(i,e))throw new Error(`The selected capture source does not look like ${e.name}. Choose the game window.`);return{stream:t,label:i,displaySurface:a??"window"}}function kt(){let t=["video/webm;codecs=vp8","video/webm;codecs=vp8,opus","video/webm;codecs=vp9,opus","video/webm;codecs=vp9",Me],e=globalThis.MediaRecorder,o=t.find(n=>e?.isTypeSupported?.(n));if(!o)throw new Error("This Overwolf runtime cannot record playable WebM chunks from browser window capture.");return o}function bt(t,e){let o=E(t);return[e.name,e.windowTitle,e.processName].map(r=>E(r??"")).filter(Boolean).some(r=>o.includes(r)||r.includes(o)||o.includes(r.replace(/playerbeta$/,"")))}function Lt(t,e){let o=u(t.ForegroundProcessId??t.foregroundProcessId);if(e.processId!==void 0)return o===e.processId;let n=l(t.ForegroundProcessName??t.foregroundProcessName??t.foregroundProcess);if(n&&e.processName){let a=E(Be(n)??n),i=E(e.processName);if(a&&i&&(a===i||a.includes(i)))return!0}let r=l(t.ForegroundWindowTitle??t.foregroundWindowTitle??t.foregroundTitle);if(r){let a=E(r);return[e.windowTitle,e.name].map(s=>E(s??"")).filter(Boolean).some(s=>a===s||a.includes(s))}return!1}function St(t,e){let o=It(e);if(o){if(e.state==="released"){t.nativeHeldInputKeys.delete(o);return}t.nativeHeldInputKeys.add(o)}}function It(t){return t.deviceType==="mouse"&&typeof t.mouseButton=="number"?`mouse:${t.mouseButton}`:typeof t.virtualKey=="number"?`vk:${t.virtualKey}`:typeof t.keyCode=="number"?`key:${t.keyCode}`:typeof t.scanCode=="number"?`scan:${t.scanCode}`:null}function Mt(t){return/^(screen|display|desktop|monitor|entire screen)(:|$)/i.test((t??"").trim())}function Ot(t){return/^(window|web-contents-media-stream)(:|$)/i.test(t.trim())}function _(t){for(let e of t?.stream?.getTracks()??[])try{e.stop()}catch{}}function _t(t){return l(t.file_name??t.fileName??t.file_path??t.filePath??t.path)}function ke(t,e){if(e!==void 0&&Number.isInteger(e)&&e>=0&&!t.uploadedSegments.some(r=>r.index===e))return e;let o=new Set(t.uploadedSegments.map(r=>r.index)),n=0;for(;o.has(n);)n+=1;return n}function Ge(t){return t.uploadedSegments.reduce((e,o)=>e+o.durationMs,0)}function Ct(t,e){let o=[...t.uploadedSegments].sort((r,a)=>r.index-a.index),n=Math.max(t.segmentSeconds,...o.map(r=>Math.ceil(r.durationMs/1e3)));return["#EXTM3U","#EXT-X-VERSION:7","#EXT-X-LOOKLOOT-SEQUENTIAL-MEDIA:1","#EXT-X-LOOKLOOT-SEQUENTIAL-MP4:1",...o[0]?.contentType?[`#EXT-X-LOOKLOOT-SEGMENT-CONTENT-TYPE:${o[0].contentType}`]:[],`#EXT-X-TARGETDURATION:${Math.max(1,Math.ceil(n))}`,"#EXT-X-MEDIA-SEQUENCE:0","#EXT-X-PLAYLIST-TYPE:EVENT",...o.flatMap(r=>[`#EXTINF:${(r.durationMs/1e3).toFixed(3)},`,r.publicUrl??r.uri]),...e.final?["#EXT-X-ENDLIST"]:[],""].join(`
4
+ `)}function G(t,e){if(!t)return null;let o=t.uploadedSegments[0];return{provider:"chunked_hls",source:t.source,status:t.status,segmentSeconds:t.segmentSeconds,segmentContentType:o?.contentType??null,segmentContainer:o?o.uri.toLowerCase().endsWith(".webm")?"webm":"mp4":null,playlistKey:t.uploadedPlaylistKey,uploadedPlaylistKey:t.uploadedPlaylistKey,uploadedSegmentCount:t.uploadedSegments.length,durationMs:(e??Ge(t))||null,lastError:t.lastError}}function be(t){return t?[...t.uploadedSegments.map(e=>e.objectKey),...t.uploadedPlaylistKey?[t.uploadedPlaylistKey]:[]]:[]}function Et(t){return t?[...t.uploadedSegments.map(e=>({kind:"video",objectKey:e.objectKey,contentType:e.contentType,sizeBytes:e.sizeBytes})),...t.uploadedPlaylistKey?[{kind:"video",objectKey:t.uploadedPlaylistKey,contentType:"application/vnd.apple.mpegurl"}]:[]]:[]}function Pt(t){return!!t?.objectKey}function Nt(t){return t.trim().replace(/\\/g,"/").toLowerCase()}function xt(t){return p(t)&&p(t.video)&&p(t.input)&&p(t.upload)}function Tt(){let t=globalThis;return t.window??(typeof t.open=="function"?globalThis:null)}function Rt(t){try{return typeof Blob!="function"||typeof URL>"u"||typeof URL.createObjectURL!="function"?null:URL.createObjectURL(new Blob([t],{type:"text/html;charset=utf-8"}))}catch{return null}}function Bt(t){return`data:text/html;charset=utf-8,${encodeURIComponent(t)}`}function Ut(t,e="LookLoot Overwolf Native Verification"){let o=Gt(t),n=t.ok?"Passed":"Needs attention",r=[["Overall",n,t.reason],["Video",t.video.ok?"OK":"Missing",t.video.reason],["Input",`${t.input.eventCount} event(s)`,t.input.reason],["Game events",`${t.gameState.eventCount} event(s), ${t.gameState.snapshotCount} snapshot(s)`,t.gameState.reason],["Upload","Skipped",t.upload.reason]];return`<!doctype html>
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <title>${k(e)}</title>
10
+ <style>
11
+ :root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #101214; color: #f6f4ee; }
12
+ body { margin: 0; padding: 24px; }
13
+ main { max-width: 920px; margin: 0 auto; }
14
+ h1 { font-size: 24px; margin: 0 0 8px; }
15
+ .muted { color: #aaa59a; }
16
+ .status { display: inline-flex; margin: 12px 0 20px; padding: 6px 10px; border-radius: 6px; background: ${t.ok?"#17452e":"#4b261b"}; color: #fff; font-weight: 700; }
17
+ video { width: 100%; max-height: 520px; border-radius: 8px; background: #050607; margin: 12px 0 18px; }
18
+ table { width: 100%; border-collapse: collapse; margin: 18px 0; }
19
+ th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid #2c3035; vertical-align: top; }
20
+ th { color: #cfc8ba; font-size: 13px; text-transform: uppercase; }
21
+ code, pre { font-family: "SFMono-Regular", Consolas, monospace; }
22
+ code { color: #f1d38a; }
23
+ pre { white-space: pre-wrap; background: #171a1d; border: 1px solid #2c3035; border-radius: 8px; padding: 14px; overflow: auto; }
24
+ </style>
25
+ </head>
26
+ <body>
27
+ <main>
28
+ <h1>${k(e)}</h1>
29
+ <div class="muted">Generated ${k(new Date().toISOString())}</div>
30
+ <div class="status">${k(n)}</div>
31
+ ${o?`<video controls src="${Dt(o)}"></video>`:'<p class="muted">No browser-playable media URL was available. Overwolf returned the replay metadata below.</p>'}
32
+ <table>
33
+ <thead><tr><th>Check</th><th>Evidence</th><th>Notes</th></tr></thead>
34
+ <tbody>
35
+ ${r.map(([a,i,s])=>`<tr><td>${k(a)}</td><td>${k(i)}</td><td>${k(s??"")}</td></tr>`).join("")}
36
+ </tbody>
37
+ </table>
38
+ <h2>Replay</h2>
39
+ <pre>${k(JSON.stringify(t.video,null,2))}</pre>
40
+ <h2>Game</h2>
41
+ <pre>${k(JSON.stringify(t.game??null,null,2))}</pre>
42
+ <h2>Diagnostics</h2>
43
+ <pre>${k(JSON.stringify(t.diagnostics,null,2))}</pre>
44
+ </main>
45
+ </body>
46
+ </html>`}function Gt(t){let e=t.video.mediaUrl?.trim();if(e&&/^(?:https?:|blob:|data:)/i.test(e))return e}function k(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Dt(t){return k(t).replace(/"/g,"&quot;")}function D(){let t=globalThis;return t.window?.looklootOverwolfUploadBridge??t.looklootOverwolfUploadBridge}function At(){try{let t=globalThis.localStorage;if(!t)return!1;let e="lookloot:storage-check";return t.setItem(e,"1"),t.removeItem(e),!0}catch{return!1}}function Vt(t){let e=t?.trim();if(e)return e.slice(0,240);try{let o=globalThis.localStorage?.getItem(ce)?.trim();if(o)return o.slice(0,240);let n=Le();return globalThis.localStorage?.setItem(ce,n),n}catch{return Le()}}function Le(){if(typeof globalThis.crypto?.randomUUID=="function")return`anon_${globalThis.crypto.randomUUID()}`;let t=new Uint8Array(16);return typeof globalThis.crypto?.getRandomValues=="function"?(globalThis.crypto.getRandomValues(t),`anon_${Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}`):`anon_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`}var De=[];function Q(){try{let t=globalThis.localStorage?.getItem(Se);if(!t)return[];let e=JSON.parse(t);return Array.isArray(e)?e:[]}catch{return De}}function A(t){try{globalThis.localStorage?.setItem(Se,JSON.stringify(t))}catch{De=t}}function I(t){return t instanceof Error?t.message:String(t)}export{ee as LookLootCapture,qe as createLookLootOverwolfNativeInputSource,Kt as createLookLootOverwolfNativeIntegration,He as detectLookLootOverwolfNativeProcessGames,pe as getLookLootOverwolfRunningGame,me as requestLookLootOverwolfGameWindowSource,F as resolveLookLootOverwolfNativeProcessPlugin};
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@lookloot/capture-sdk-overwolf-native",
3
+ "version": "0.1.0",
4
+ "description": "LookLoot Capture SDK adapter for classic Overwolf Native apps.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "prebuild": "node ../../scripts/run-pnpm.mjs --dir ../capture-contracts build",
20
+ "build": "tsup",
21
+ "prepublishOnly": "pnpm run build && npm pack --dry-run"
22
+ },
23
+ "keywords": [
24
+ "lookloot",
25
+ "capture",
26
+ "sdk",
27
+ "overwolf",
28
+ "overwolf-native"
29
+ ],
30
+ "license": "UNLICENSED",
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "dependencies": {
35
+ "@lookloot/capture-contracts": "workspace:*"
36
+ },
37
+ "devDependencies": {
38
+ "tsup": "^8.0.0",
39
+ "typescript": "^5"
40
+ }
41
+ }