@apocaliss92/scrypted-reolink-native 0.4.3 → 0.4.4

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/dist/plugin.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apocaliss92/scrypted-reolink-native",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Use any reolink camera with Scrypted, even older/unsupported models without HTTP protocol support",
5
5
  "author": "@apocaliss92",
6
6
  "license": "Apache",
package/src/utils.ts CHANGED
@@ -202,9 +202,9 @@ export async function recordingFileToVideoClip(
202
202
  resources:
203
203
  videoHref || thumbnailHref
204
204
  ? {
205
- ...(videoHref ? { video: { href: videoHref } } : {}),
206
- ...(thumbnailHref ? { thumbnail: { href: thumbnailHref } } : {}),
207
- }
205
+ ...(videoHref ? { video: { href: videoHref } } : {}),
206
+ ...(thumbnailHref ? { thumbnail: { href: thumbnailHref } } : {}),
207
+ }
208
208
  : undefined,
209
209
  };
210
210
  }
@@ -311,6 +311,21 @@ export async function getVideoClipWebhookUrls(props: {
311
311
  }
312
312
  }
313
313
 
314
+ const getHeader = (headers: Record<string, any> | undefined, key: string) => {
315
+ return headers?.[key] ?? headers?.[key.toLowerCase()] ?? headers?.[key.toUpperCase()];
316
+ };
317
+
318
+ export const getVideoclipClientInfo = (request: HttpRequest) => {
319
+ return {
320
+ userAgent: getHeader(request.headers, 'user-agent') ?? getHeader(request.headers, 'User-Agent'),
321
+ accept: getHeader(request.headers, 'accept') ?? getHeader(request.headers, 'Accept'),
322
+ range: getHeader(request.headers, 'range') ?? getHeader(request.headers, 'Range'),
323
+ secChUa: getHeader(request.headers, 'sec-ch-ua') ?? getHeader(request.headers, 'Sec-CH-UA'),
324
+ secChUaMobile: getHeader(request.headers, 'sec-ch-ua-mobile') ?? getHeader(request.headers, 'Sec-CH-UA-Mobile'),
325
+ secChUaPlatform: getHeader(request.headers, 'sec-ch-ua-platform') ?? getHeader(request.headers, 'Sec-CH-UA-Platform'),
326
+ };
327
+ };
328
+
314
329
  /**
315
330
  * Handle video clip webhook request
316
331
  * Uses progressive streaming for immediate playback.
@@ -380,6 +395,11 @@ export async function handleVideoClipRequest(props: {
380
395
  };
381
396
  api.client.on("error", onClientError);
382
397
 
398
+ const clientInfo = getVideoclipClientInfo(request);
399
+ const ua = (clientInfo.userAgent ?? '').toLowerCase();
400
+ const isIos = /iphone|ipad|ipod/.test(ua);
401
+ const isIosInstalledApp = ua.includes('installedapp');
402
+
383
403
  // Use streaming replay - this starts immediately and produces fMP4 chunks
384
404
  // Stream management (stopping previous streams, cooldown) is handled by the API layer
385
405
  // Generate a unique session ID based on client fingerprint (UA + IP + other factors)
@@ -387,8 +407,8 @@ export async function handleVideoClipRequest(props: {
387
407
  const clientFingerprint = [
388
408
  request.headers?.["user-agent"] || "",
389
409
  request.headers?.["x-forwarded-for"] ||
390
- request.headers?.["x-real-ip"] ||
391
- "",
410
+ request.headers?.["x-real-ip"] ||
411
+ "",
392
412
  request.headers?.["accept-language"] || "",
393
413
  request.headers?.["accept-encoding"] || "",
394
414
  ].join("|");
@@ -405,6 +425,7 @@ export async function handleVideoClipRequest(props: {
405
425
  isNvr: device.isOnNvr,
406
426
  logger,
407
427
  deviceId: sessionId,
428
+ transcodeH265ToH264: isIos && isIosInstalledApp,
408
429
  });
409
430
 
410
431
  let totalSize = 0;
@@ -422,7 +443,7 @@ export async function handleVideoClipRequest(props: {
422
443
  } finally {
423
444
  // Remove the error handler
424
445
  api.client.off("error", onClientError);
425
- await stop().catch(() => {});
446
+ await stop().catch(() => { });
426
447
  }
427
448
  })(),
428
449
  {