@camstack/addon-provider-reolink 1.1.12 → 1.1.14
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/addon.js +288 -19
- package/dist/addon.mjs +288 -19
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -4655,7 +4655,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4655
4655
|
return inst;
|
|
4656
4656
|
}
|
|
4657
4657
|
//#endregion
|
|
4658
|
-
//#region ../types/dist/sleep-
|
|
4658
|
+
//#region ../types/dist/sleep-MHm--th-.mjs
|
|
4659
4659
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4660
4660
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4661
4661
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5917,6 +5917,7 @@ var CamStreamKindSchema = _enum([
|
|
|
5917
5917
|
"pull-rtsp",
|
|
5918
5918
|
"pull-rtmp",
|
|
5919
5919
|
"pull-http",
|
|
5920
|
+
"pull-flv",
|
|
5920
5921
|
"pull-rfc4571",
|
|
5921
5922
|
"push-annexb",
|
|
5922
5923
|
"derived"
|
|
@@ -7263,7 +7264,21 @@ var StorageLocationDeclarationSchema = object({
|
|
|
7263
7264
|
* slots (e.g. `recordingsLow` → `recordings`) so operators only need to
|
|
7264
7265
|
* configure the primary location.
|
|
7265
7266
|
*/
|
|
7266
|
-
defaultsTo: string().optional()
|
|
7267
|
+
defaultsTo: string().optional(),
|
|
7268
|
+
/**
|
|
7269
|
+
* Which node root the seeded `<id>:default` instance is placed under on a
|
|
7270
|
+
* FRESH install:
|
|
7271
|
+
* - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
|
|
7272
|
+
* the appData volume. Right for small/durable data (backups, logs, models).
|
|
7273
|
+
* - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
|
|
7274
|
+
* env is set, else falls back to the data root. Right for bulky, hot media
|
|
7275
|
+
* (recordings, event media) that should stay off the appData disk.
|
|
7276
|
+
*
|
|
7277
|
+
* Only affects the seeded default's `basePath`; operators can repoint any
|
|
7278
|
+
* location afterwards, and a `defaultsTo` slot inherits its parent's root
|
|
7279
|
+
* regardless of this field. Absent (the common case) is treated as `'data'`.
|
|
7280
|
+
*/
|
|
7281
|
+
defaultRoot: _enum(["data", "media"]).optional()
|
|
7267
7282
|
});
|
|
7268
7283
|
var DecoderStatsSchema = object({
|
|
7269
7284
|
inputFps: number(),
|
|
@@ -8658,6 +8673,10 @@ var RtspRestreamEntrySchema = object({
|
|
|
8658
8673
|
var BrokerRtspClientSchema = object({
|
|
8659
8674
|
sessionId: string(),
|
|
8660
8675
|
remoteAddr: string(),
|
|
8676
|
+
/** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
|
|
8677
|
+
* null/absent when the client sent none. Lets the UI label a consumer by
|
|
8678
|
+
* purpose. Optional so a client built against an older schema stays valid. */
|
|
8679
|
+
userAgent: string().nullish(),
|
|
8661
8680
|
playing: boolean(),
|
|
8662
8681
|
muted: boolean(),
|
|
8663
8682
|
connectedAt: number(),
|
|
@@ -16273,7 +16292,7 @@ var AddBrokerInputSchema = object({
|
|
|
16273
16292
|
});
|
|
16274
16293
|
var AddBrokerResultSchema = object({ id: string() });
|
|
16275
16294
|
var IdInputSchema = object({ id: string() });
|
|
16276
|
-
var TestResultSchema = discriminatedUnion("ok", [object({
|
|
16295
|
+
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
16277
16296
|
ok: literal(true),
|
|
16278
16297
|
latencyMs: number()
|
|
16279
16298
|
}), object({
|
|
@@ -16296,7 +16315,7 @@ var StatusSchema = object({
|
|
|
16296
16315
|
brokerCount: number(),
|
|
16297
16316
|
embeddedRunning: boolean()
|
|
16298
16317
|
});
|
|
16299
|
-
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
|
|
16318
|
+
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
|
|
16300
16319
|
var NetworkEndpointSchema = object({
|
|
16301
16320
|
url: string(),
|
|
16302
16321
|
hostname: string(),
|
|
@@ -16330,23 +16349,198 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
|
16330
16349
|
sourcePort: number().optional()
|
|
16331
16350
|
});
|
|
16332
16351
|
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
16333
|
-
|
|
16334
|
-
|
|
16352
|
+
/**
|
|
16353
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
16354
|
+
*
|
|
16355
|
+
* Apprise-derived model (see
|
|
16356
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
16357
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
16358
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
16359
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
16360
|
+
* message to what the kind supports — callers never special-case a service.
|
|
16361
|
+
*
|
|
16362
|
+
* DESIGN DECISIONS (locked):
|
|
16363
|
+
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
16364
|
+
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
16365
|
+
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
16366
|
+
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
16367
|
+
* alternative would fork the UI per addon and cannot host the
|
|
16368
|
+
* discovery→adopt flow.
|
|
16369
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
16370
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
16371
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
16372
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
16373
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
16374
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
16375
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
16376
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
16377
|
+
* base64 fallback needed.
|
|
16378
|
+
*
|
|
16379
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
16380
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
16381
|
+
* admin "Integrations" page.
|
|
16382
|
+
*/
|
|
16383
|
+
/**
|
|
16384
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
16385
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
16386
|
+
*/
|
|
16387
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
16388
|
+
"image",
|
|
16389
|
+
"video",
|
|
16390
|
+
"gif",
|
|
16391
|
+
"audio",
|
|
16392
|
+
"icon"
|
|
16393
|
+
]);
|
|
16394
|
+
/**
|
|
16395
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
16396
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
16397
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
16398
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
16399
|
+
*/
|
|
16400
|
+
var AttachmentSchema = object({
|
|
16401
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
16402
|
+
url: string().optional(),
|
|
16403
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
16404
|
+
mime: string().optional(),
|
|
16405
|
+
name: string().optional()
|
|
16406
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
16407
|
+
var NotificationFormatSchema = _enum([
|
|
16408
|
+
"text",
|
|
16409
|
+
"markdown",
|
|
16410
|
+
"html"
|
|
16411
|
+
]);
|
|
16412
|
+
/** A single tap-through action button. */
|
|
16413
|
+
var NotificationActionSchema = object({
|
|
16414
|
+
id: string(),
|
|
16415
|
+
label: string(),
|
|
16416
|
+
url: string().optional()
|
|
16417
|
+
});
|
|
16418
|
+
/**
|
|
16419
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
16420
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
16421
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
16422
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
16423
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
16424
|
+
* `priority` for that one target.
|
|
16425
|
+
*/
|
|
16426
|
+
var NotificationSchema = object({
|
|
16335
16427
|
body: string(),
|
|
16336
|
-
|
|
16428
|
+
title: string().optional(),
|
|
16429
|
+
format: NotificationFormatSchema.default("text"),
|
|
16430
|
+
priority: number().int().min(1).max(5).default(3),
|
|
16431
|
+
level: string().optional(),
|
|
16432
|
+
attachments: array(AttachmentSchema).optional(),
|
|
16433
|
+
clickUrl: string().optional(),
|
|
16434
|
+
actions: array(NotificationActionSchema).optional(),
|
|
16435
|
+
sound: string().optional(),
|
|
16436
|
+
ttl: number().optional(),
|
|
16437
|
+
tag: string().optional(),
|
|
16337
16438
|
deviceId: number().optional(),
|
|
16338
16439
|
eventId: string().optional(),
|
|
16339
|
-
priority: _enum([
|
|
16340
|
-
"low",
|
|
16341
|
-
"normal",
|
|
16342
|
-
"high",
|
|
16343
|
-
"critical"
|
|
16344
|
-
]).default("normal"),
|
|
16345
16440
|
metadata: record(string(), unknown()).optional()
|
|
16346
|
-
})
|
|
16441
|
+
});
|
|
16442
|
+
/** One declared native severity/priority level for a kind. */
|
|
16443
|
+
var TargetKindLevelSchema = object({
|
|
16444
|
+
id: string(),
|
|
16445
|
+
label: string(),
|
|
16446
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
16447
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
16448
|
+
flags: object({
|
|
16449
|
+
critical: boolean().optional(),
|
|
16450
|
+
silent: boolean().optional(),
|
|
16451
|
+
noPush: boolean().optional()
|
|
16452
|
+
}).optional(),
|
|
16453
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
16454
|
+
requires: array(string()).optional(),
|
|
16455
|
+
description: string().optional()
|
|
16456
|
+
});
|
|
16457
|
+
/** The full capability block consulted before dispatch. */
|
|
16458
|
+
var TargetKindCapsSchema = object({
|
|
16459
|
+
attachments: object({
|
|
16460
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
16461
|
+
mode: _enum([
|
|
16462
|
+
"url",
|
|
16463
|
+
"bytes",
|
|
16464
|
+
"both"
|
|
16465
|
+
]),
|
|
16466
|
+
max: number().int().nonnegative(),
|
|
16467
|
+
maxBytes: number().int().positive().optional()
|
|
16468
|
+
}),
|
|
16469
|
+
/** Max action buttons (0 = none). */
|
|
16470
|
+
actions: number().int().nonnegative(),
|
|
16471
|
+
levels: array(TargetKindLevelSchema),
|
|
16472
|
+
format: array(NotificationFormatSchema),
|
|
16473
|
+
clickUrl: boolean(),
|
|
16474
|
+
sound: boolean(),
|
|
16475
|
+
ttl: boolean(),
|
|
16476
|
+
bodyMaxLen: number().int().positive()
|
|
16477
|
+
});
|
|
16478
|
+
/**
|
|
16479
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
16480
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
16481
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
16482
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
16483
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
16484
|
+
*/
|
|
16485
|
+
var ConfigSchemaPassthrough = unknown();
|
|
16486
|
+
var TargetKindSchema = object({
|
|
16487
|
+
kind: string(),
|
|
16488
|
+
label: string(),
|
|
16489
|
+
icon: string(),
|
|
16490
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
16491
|
+
addonId: string(),
|
|
16492
|
+
configSchema: ConfigSchemaPassthrough,
|
|
16493
|
+
supportsDiscovery: boolean(),
|
|
16494
|
+
caps: TargetKindCapsSchema
|
|
16495
|
+
});
|
|
16496
|
+
/**
|
|
16497
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
16498
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
16499
|
+
* round-trip a stored secret to the UI.
|
|
16500
|
+
*/
|
|
16501
|
+
var TargetSchema = object({
|
|
16502
|
+
id: string(),
|
|
16503
|
+
name: string(),
|
|
16504
|
+
kind: string(),
|
|
16505
|
+
addonId: string(),
|
|
16506
|
+
enabled: boolean(),
|
|
16507
|
+
config: record(string(), unknown())
|
|
16508
|
+
});
|
|
16509
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
16510
|
+
var DiscoveredTargetSchema = object({
|
|
16511
|
+
kind: string(),
|
|
16512
|
+
suggestedName: string(),
|
|
16513
|
+
config: record(string(), unknown())
|
|
16514
|
+
});
|
|
16515
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
16516
|
+
var RenderedAsSchema = object({
|
|
16517
|
+
level: string(),
|
|
16518
|
+
format: NotificationFormatSchema,
|
|
16519
|
+
attachmentsSent: number().int().nonnegative(),
|
|
16520
|
+
actionsSent: number().int().nonnegative(),
|
|
16521
|
+
truncated: boolean(),
|
|
16522
|
+
dropped: array(string())
|
|
16523
|
+
});
|
|
16524
|
+
var SendResultSchema = object({
|
|
16347
16525
|
success: boolean(),
|
|
16348
|
-
error: string().optional()
|
|
16349
|
-
|
|
16526
|
+
error: string().optional(),
|
|
16527
|
+
renderedAs: RenderedAsSchema.optional()
|
|
16528
|
+
});
|
|
16529
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
16530
|
+
var TestResultSchema = SendResultSchema;
|
|
16531
|
+
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema)), method(object({
|
|
16532
|
+
kind: string(),
|
|
16533
|
+
config: record(string(), unknown()).optional()
|
|
16534
|
+
}), array(DiscoveredTargetSchema)), method(object({
|
|
16535
|
+
targetId: string(),
|
|
16536
|
+
notification: NotificationSchema
|
|
16537
|
+
}), SendResultSchema, { kind: "mutation" }), method(object({
|
|
16538
|
+
targetId: string(),
|
|
16539
|
+
sample: NotificationSchema.optional()
|
|
16540
|
+
}), TestResultSchema, { kind: "mutation" }), method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }), method(object({ targetId: string() }), _void(), { kind: "mutation" }), method(object({
|
|
16541
|
+
targetId: string(),
|
|
16542
|
+
enabled: boolean()
|
|
16543
|
+
}), _void(), { kind: "mutation" });
|
|
16350
16544
|
/**
|
|
16351
16545
|
* Zod schemas for persisted record types.
|
|
16352
16546
|
*
|
|
@@ -22713,13 +22907,49 @@ Object.freeze({
|
|
|
22713
22907
|
addonId: null,
|
|
22714
22908
|
access: "create"
|
|
22715
22909
|
},
|
|
22910
|
+
"notificationOutput.deleteTarget": {
|
|
22911
|
+
capName: "notification-output",
|
|
22912
|
+
capScope: "system",
|
|
22913
|
+
addonId: null,
|
|
22914
|
+
access: "delete"
|
|
22915
|
+
},
|
|
22916
|
+
"notificationOutput.discoverTargets": {
|
|
22917
|
+
capName: "notification-output",
|
|
22918
|
+
capScope: "system",
|
|
22919
|
+
addonId: null,
|
|
22920
|
+
access: "view"
|
|
22921
|
+
},
|
|
22922
|
+
"notificationOutput.listTargetKinds": {
|
|
22923
|
+
capName: "notification-output",
|
|
22924
|
+
capScope: "system",
|
|
22925
|
+
addonId: null,
|
|
22926
|
+
access: "view"
|
|
22927
|
+
},
|
|
22928
|
+
"notificationOutput.listTargets": {
|
|
22929
|
+
capName: "notification-output",
|
|
22930
|
+
capScope: "system",
|
|
22931
|
+
addonId: null,
|
|
22932
|
+
access: "view"
|
|
22933
|
+
},
|
|
22716
22934
|
"notificationOutput.send": {
|
|
22717
22935
|
capName: "notification-output",
|
|
22718
22936
|
capScope: "system",
|
|
22719
22937
|
addonId: null,
|
|
22720
22938
|
access: "create"
|
|
22721
22939
|
},
|
|
22722
|
-
"notificationOutput.
|
|
22940
|
+
"notificationOutput.setTargetEnabled": {
|
|
22941
|
+
capName: "notification-output",
|
|
22942
|
+
capScope: "system",
|
|
22943
|
+
addonId: null,
|
|
22944
|
+
access: "create"
|
|
22945
|
+
},
|
|
22946
|
+
"notificationOutput.testTarget": {
|
|
22947
|
+
capName: "notification-output",
|
|
22948
|
+
capScope: "system",
|
|
22949
|
+
addonId: null,
|
|
22950
|
+
access: "create"
|
|
22951
|
+
},
|
|
22952
|
+
"notificationOutput.upsertTarget": {
|
|
22723
22953
|
capName: "notification-output",
|
|
22724
22954
|
capScope: "system",
|
|
22725
22955
|
addonId: null,
|
|
@@ -215218,7 +215448,7 @@ function isNativeObjectForwardingEnabled(capState) {
|
|
|
215218
215448
|
}
|
|
215219
215449
|
//#endregion
|
|
215220
215450
|
//#region src/stream-routing.ts
|
|
215221
|
-
var KIND_RE = /^(native|rtsp|rtmp):(.+)$/;
|
|
215451
|
+
var KIND_RE = /^(native|rtsp|rtmp|flv):(.+)$/;
|
|
215222
215452
|
var CH_PROFILE_RE = /^ch(\d+)-(main|sub|ext)$/;
|
|
215223
215453
|
var PROFILE_ONLY_RE = /^(main|sub|ext)$/;
|
|
215224
215454
|
/**
|
|
@@ -215268,6 +215498,7 @@ function kindLabel(kind) {
|
|
|
215268
215498
|
case "native": return "Native";
|
|
215269
215499
|
case "rtsp": return "RTSP";
|
|
215270
215500
|
case "rtmp": return "RTMP";
|
|
215501
|
+
case "flv": return "FLV";
|
|
215271
215502
|
}
|
|
215272
215503
|
}
|
|
215273
215504
|
/**
|
|
@@ -218714,7 +218945,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
218714
218945
|
*/
|
|
218715
218946
|
resolveAudioCodecApi() {
|
|
218716
218947
|
const router = this.ctx.api.audioCodec;
|
|
218717
|
-
if (!router) throw new Error("Reolink intercom: `audio-codec` capability is not mounted. Install + enable the `addon-audio-codec-
|
|
218948
|
+
if (!router) throw new Error("Reolink intercom: `audio-codec` capability is not mounted. Install + enable the `addon-audio-codec-ffmpeg` addon (or any other addon that provides the `audio-codec` capability) before opening a talk session.");
|
|
218718
218949
|
return {
|
|
218719
218950
|
createDecodeSession: (input) => router.createDecodeSession.mutate(input),
|
|
218720
218951
|
pushEncodedFrame: (input) => router.pushEncodedFrame.mutate(input),
|
|
@@ -219154,6 +219385,23 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
219154
219385
|
}
|
|
219155
219386
|
}
|
|
219156
219387
|
/**
|
|
219388
|
+
* Synthesize the HTTP-FLV pull URL for a (channel, profile) pair. FLV is
|
|
219389
|
+
* served off the Reolink Baichuan media port (default 1935, app `bcs`) as
|
|
219390
|
+
* `channel<channel>_<profile>.bcs` — the same shape Frigate/go2rtc use. The
|
|
219391
|
+
* lib does NOT surface this URL, so we build it from the SAME connection
|
|
219392
|
+
* creds the RTSP/native paths use (`host`/`username`/`password` on the
|
|
219393
|
+
* device config), URL-encoding the credentials.
|
|
219394
|
+
*
|
|
219395
|
+
* Returns `null` when the host isn't locally resolvable (hub-child cameras
|
|
219396
|
+
* borrow the parent's socket and hold no own `host`), so the caller skips
|
|
219397
|
+
* FLV publication for those devices rather than emitting a broken URL.
|
|
219398
|
+
*/
|
|
219399
|
+
buildFlvUrl(channel, profile) {
|
|
219400
|
+
const host = this.config.get("host");
|
|
219401
|
+
if (typeof host !== "string" || host.length === 0) return null;
|
|
219402
|
+
return `http://${host}/flv?port=1935&app=bcs&stream=channel${channel}_${profile}.bcs&user=${encodeURIComponent(String(this.config.get("username") ?? ""))}&password=${encodeURIComponent(String(this.config.get("password") ?? ""))}`;
|
|
219403
|
+
}
|
|
219404
|
+
/**
|
|
219157
219405
|
* Heavy path: talk to the camera and assemble the descriptors. Only ever
|
|
219158
219406
|
* entered when there is no cached catalog (see `buildStreamCatalog`).
|
|
219159
219407
|
*
|
|
@@ -219239,6 +219487,27 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
219239
219487
|
};
|
|
219240
219488
|
enqueueAlt("rtsp", streamOptions.rtspStreams);
|
|
219241
219489
|
enqueueAlt("rtmp", streamOptions.rtmpStreams);
|
|
219490
|
+
for (const s of streamOptions.nativeStreams) {
|
|
219491
|
+
const channel = s.channel ?? (isChild ? ownChannel : 0);
|
|
219492
|
+
const id = buildCamStreamId("flv", channel, s.profile, channelCount);
|
|
219493
|
+
if (desired.has(id)) continue;
|
|
219494
|
+
const flvUrl = this.buildFlvUrl(channel, s.profile);
|
|
219495
|
+
if (!flvUrl) break;
|
|
219496
|
+
const m = s.metadata;
|
|
219497
|
+
desired.set(id, {
|
|
219498
|
+
camStreamId: id,
|
|
219499
|
+
kind: "pull-flv",
|
|
219500
|
+
label: streamLabel(s, "flv"),
|
|
219501
|
+
url: flvUrl,
|
|
219502
|
+
autoEligible: false,
|
|
219503
|
+
...m && m.width > 0 && m.height > 0 ? { resolution: {
|
|
219504
|
+
width: m.width,
|
|
219505
|
+
height: m.height
|
|
219506
|
+
} } : {},
|
|
219507
|
+
...m && m.frameRate > 0 ? { fps: m.frameRate } : {},
|
|
219508
|
+
...normalizeCodecName(m?.videoEncType) ? { codec: normalizeCodecName(m?.videoEncType) } : {}
|
|
219509
|
+
});
|
|
219510
|
+
}
|
|
219242
219511
|
if (desired.size === 0) {
|
|
219243
219512
|
this.ctx.logger.warn("buildStreamCatalog: camera reports no streams — empty catalog", { tags: { deviceId: this.id } });
|
|
219244
219513
|
return [];
|
package/dist/addon.mjs
CHANGED
|
@@ -4650,7 +4650,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4650
4650
|
return inst;
|
|
4651
4651
|
}
|
|
4652
4652
|
//#endregion
|
|
4653
|
-
//#region ../types/dist/sleep-
|
|
4653
|
+
//#region ../types/dist/sleep-MHm--th-.mjs
|
|
4654
4654
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4655
4655
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4656
4656
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5912,6 +5912,7 @@ var CamStreamKindSchema = _enum([
|
|
|
5912
5912
|
"pull-rtsp",
|
|
5913
5913
|
"pull-rtmp",
|
|
5914
5914
|
"pull-http",
|
|
5915
|
+
"pull-flv",
|
|
5915
5916
|
"pull-rfc4571",
|
|
5916
5917
|
"push-annexb",
|
|
5917
5918
|
"derived"
|
|
@@ -7258,7 +7259,21 @@ var StorageLocationDeclarationSchema = object({
|
|
|
7258
7259
|
* slots (e.g. `recordingsLow` → `recordings`) so operators only need to
|
|
7259
7260
|
* configure the primary location.
|
|
7260
7261
|
*/
|
|
7261
|
-
defaultsTo: string().optional()
|
|
7262
|
+
defaultsTo: string().optional(),
|
|
7263
|
+
/**
|
|
7264
|
+
* Which node root the seeded `<id>:default` instance is placed under on a
|
|
7265
|
+
* FRESH install:
|
|
7266
|
+
* - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
|
|
7267
|
+
* the appData volume. Right for small/durable data (backups, logs, models).
|
|
7268
|
+
* - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
|
|
7269
|
+
* env is set, else falls back to the data root. Right for bulky, hot media
|
|
7270
|
+
* (recordings, event media) that should stay off the appData disk.
|
|
7271
|
+
*
|
|
7272
|
+
* Only affects the seeded default's `basePath`; operators can repoint any
|
|
7273
|
+
* location afterwards, and a `defaultsTo` slot inherits its parent's root
|
|
7274
|
+
* regardless of this field. Absent (the common case) is treated as `'data'`.
|
|
7275
|
+
*/
|
|
7276
|
+
defaultRoot: _enum(["data", "media"]).optional()
|
|
7262
7277
|
});
|
|
7263
7278
|
var DecoderStatsSchema = object({
|
|
7264
7279
|
inputFps: number(),
|
|
@@ -8653,6 +8668,10 @@ var RtspRestreamEntrySchema = object({
|
|
|
8653
8668
|
var BrokerRtspClientSchema = object({
|
|
8654
8669
|
sessionId: string(),
|
|
8655
8670
|
remoteAddr: string(),
|
|
8671
|
+
/** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
|
|
8672
|
+
* null/absent when the client sent none. Lets the UI label a consumer by
|
|
8673
|
+
* purpose. Optional so a client built against an older schema stays valid. */
|
|
8674
|
+
userAgent: string().nullish(),
|
|
8656
8675
|
playing: boolean(),
|
|
8657
8676
|
muted: boolean(),
|
|
8658
8677
|
connectedAt: number(),
|
|
@@ -16268,7 +16287,7 @@ var AddBrokerInputSchema = object({
|
|
|
16268
16287
|
});
|
|
16269
16288
|
var AddBrokerResultSchema = object({ id: string() });
|
|
16270
16289
|
var IdInputSchema = object({ id: string() });
|
|
16271
|
-
var TestResultSchema = discriminatedUnion("ok", [object({
|
|
16290
|
+
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
16272
16291
|
ok: literal(true),
|
|
16273
16292
|
latencyMs: number()
|
|
16274
16293
|
}), object({
|
|
@@ -16291,7 +16310,7 @@ var StatusSchema = object({
|
|
|
16291
16310
|
brokerCount: number(),
|
|
16292
16311
|
embeddedRunning: boolean()
|
|
16293
16312
|
});
|
|
16294
|
-
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
|
|
16313
|
+
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
|
|
16295
16314
|
var NetworkEndpointSchema = object({
|
|
16296
16315
|
url: string(),
|
|
16297
16316
|
hostname: string(),
|
|
@@ -16325,23 +16344,198 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
|
16325
16344
|
sourcePort: number().optional()
|
|
16326
16345
|
});
|
|
16327
16346
|
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
16328
|
-
|
|
16329
|
-
|
|
16347
|
+
/**
|
|
16348
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
16349
|
+
*
|
|
16350
|
+
* Apprise-derived model (see
|
|
16351
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
16352
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
16353
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
16354
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
16355
|
+
* message to what the kind supports — callers never special-case a service.
|
|
16356
|
+
*
|
|
16357
|
+
* DESIGN DECISIONS (locked):
|
|
16358
|
+
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
16359
|
+
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
16360
|
+
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
16361
|
+
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
16362
|
+
* alternative would fork the UI per addon and cannot host the
|
|
16363
|
+
* discovery→adopt flow.
|
|
16364
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
16365
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
16366
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
16367
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
16368
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
16369
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
16370
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
16371
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
16372
|
+
* base64 fallback needed.
|
|
16373
|
+
*
|
|
16374
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
16375
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
16376
|
+
* admin "Integrations" page.
|
|
16377
|
+
*/
|
|
16378
|
+
/**
|
|
16379
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
16380
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
16381
|
+
*/
|
|
16382
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
16383
|
+
"image",
|
|
16384
|
+
"video",
|
|
16385
|
+
"gif",
|
|
16386
|
+
"audio",
|
|
16387
|
+
"icon"
|
|
16388
|
+
]);
|
|
16389
|
+
/**
|
|
16390
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
16391
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
16392
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
16393
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
16394
|
+
*/
|
|
16395
|
+
var AttachmentSchema = object({
|
|
16396
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
16397
|
+
url: string().optional(),
|
|
16398
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
16399
|
+
mime: string().optional(),
|
|
16400
|
+
name: string().optional()
|
|
16401
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
16402
|
+
var NotificationFormatSchema = _enum([
|
|
16403
|
+
"text",
|
|
16404
|
+
"markdown",
|
|
16405
|
+
"html"
|
|
16406
|
+
]);
|
|
16407
|
+
/** A single tap-through action button. */
|
|
16408
|
+
var NotificationActionSchema = object({
|
|
16409
|
+
id: string(),
|
|
16410
|
+
label: string(),
|
|
16411
|
+
url: string().optional()
|
|
16412
|
+
});
|
|
16413
|
+
/**
|
|
16414
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
16415
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
16416
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
16417
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
16418
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
16419
|
+
* `priority` for that one target.
|
|
16420
|
+
*/
|
|
16421
|
+
var NotificationSchema = object({
|
|
16330
16422
|
body: string(),
|
|
16331
|
-
|
|
16423
|
+
title: string().optional(),
|
|
16424
|
+
format: NotificationFormatSchema.default("text"),
|
|
16425
|
+
priority: number().int().min(1).max(5).default(3),
|
|
16426
|
+
level: string().optional(),
|
|
16427
|
+
attachments: array(AttachmentSchema).optional(),
|
|
16428
|
+
clickUrl: string().optional(),
|
|
16429
|
+
actions: array(NotificationActionSchema).optional(),
|
|
16430
|
+
sound: string().optional(),
|
|
16431
|
+
ttl: number().optional(),
|
|
16432
|
+
tag: string().optional(),
|
|
16332
16433
|
deviceId: number().optional(),
|
|
16333
16434
|
eventId: string().optional(),
|
|
16334
|
-
priority: _enum([
|
|
16335
|
-
"low",
|
|
16336
|
-
"normal",
|
|
16337
|
-
"high",
|
|
16338
|
-
"critical"
|
|
16339
|
-
]).default("normal"),
|
|
16340
16435
|
metadata: record(string(), unknown()).optional()
|
|
16341
|
-
})
|
|
16436
|
+
});
|
|
16437
|
+
/** One declared native severity/priority level for a kind. */
|
|
16438
|
+
var TargetKindLevelSchema = object({
|
|
16439
|
+
id: string(),
|
|
16440
|
+
label: string(),
|
|
16441
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
16442
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
16443
|
+
flags: object({
|
|
16444
|
+
critical: boolean().optional(),
|
|
16445
|
+
silent: boolean().optional(),
|
|
16446
|
+
noPush: boolean().optional()
|
|
16447
|
+
}).optional(),
|
|
16448
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
16449
|
+
requires: array(string()).optional(),
|
|
16450
|
+
description: string().optional()
|
|
16451
|
+
});
|
|
16452
|
+
/** The full capability block consulted before dispatch. */
|
|
16453
|
+
var TargetKindCapsSchema = object({
|
|
16454
|
+
attachments: object({
|
|
16455
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
16456
|
+
mode: _enum([
|
|
16457
|
+
"url",
|
|
16458
|
+
"bytes",
|
|
16459
|
+
"both"
|
|
16460
|
+
]),
|
|
16461
|
+
max: number().int().nonnegative(),
|
|
16462
|
+
maxBytes: number().int().positive().optional()
|
|
16463
|
+
}),
|
|
16464
|
+
/** Max action buttons (0 = none). */
|
|
16465
|
+
actions: number().int().nonnegative(),
|
|
16466
|
+
levels: array(TargetKindLevelSchema),
|
|
16467
|
+
format: array(NotificationFormatSchema),
|
|
16468
|
+
clickUrl: boolean(),
|
|
16469
|
+
sound: boolean(),
|
|
16470
|
+
ttl: boolean(),
|
|
16471
|
+
bodyMaxLen: number().int().positive()
|
|
16472
|
+
});
|
|
16473
|
+
/**
|
|
16474
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
16475
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
16476
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
16477
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
16478
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
16479
|
+
*/
|
|
16480
|
+
var ConfigSchemaPassthrough = unknown();
|
|
16481
|
+
var TargetKindSchema = object({
|
|
16482
|
+
kind: string(),
|
|
16483
|
+
label: string(),
|
|
16484
|
+
icon: string(),
|
|
16485
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
16486
|
+
addonId: string(),
|
|
16487
|
+
configSchema: ConfigSchemaPassthrough,
|
|
16488
|
+
supportsDiscovery: boolean(),
|
|
16489
|
+
caps: TargetKindCapsSchema
|
|
16490
|
+
});
|
|
16491
|
+
/**
|
|
16492
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
16493
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
16494
|
+
* round-trip a stored secret to the UI.
|
|
16495
|
+
*/
|
|
16496
|
+
var TargetSchema = object({
|
|
16497
|
+
id: string(),
|
|
16498
|
+
name: string(),
|
|
16499
|
+
kind: string(),
|
|
16500
|
+
addonId: string(),
|
|
16501
|
+
enabled: boolean(),
|
|
16502
|
+
config: record(string(), unknown())
|
|
16503
|
+
});
|
|
16504
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
16505
|
+
var DiscoveredTargetSchema = object({
|
|
16506
|
+
kind: string(),
|
|
16507
|
+
suggestedName: string(),
|
|
16508
|
+
config: record(string(), unknown())
|
|
16509
|
+
});
|
|
16510
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
16511
|
+
var RenderedAsSchema = object({
|
|
16512
|
+
level: string(),
|
|
16513
|
+
format: NotificationFormatSchema,
|
|
16514
|
+
attachmentsSent: number().int().nonnegative(),
|
|
16515
|
+
actionsSent: number().int().nonnegative(),
|
|
16516
|
+
truncated: boolean(),
|
|
16517
|
+
dropped: array(string())
|
|
16518
|
+
});
|
|
16519
|
+
var SendResultSchema = object({
|
|
16342
16520
|
success: boolean(),
|
|
16343
|
-
error: string().optional()
|
|
16344
|
-
|
|
16521
|
+
error: string().optional(),
|
|
16522
|
+
renderedAs: RenderedAsSchema.optional()
|
|
16523
|
+
});
|
|
16524
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
16525
|
+
var TestResultSchema = SendResultSchema;
|
|
16526
|
+
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema)), method(object({
|
|
16527
|
+
kind: string(),
|
|
16528
|
+
config: record(string(), unknown()).optional()
|
|
16529
|
+
}), array(DiscoveredTargetSchema)), method(object({
|
|
16530
|
+
targetId: string(),
|
|
16531
|
+
notification: NotificationSchema
|
|
16532
|
+
}), SendResultSchema, { kind: "mutation" }), method(object({
|
|
16533
|
+
targetId: string(),
|
|
16534
|
+
sample: NotificationSchema.optional()
|
|
16535
|
+
}), TestResultSchema, { kind: "mutation" }), method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }), method(object({ targetId: string() }), _void(), { kind: "mutation" }), method(object({
|
|
16536
|
+
targetId: string(),
|
|
16537
|
+
enabled: boolean()
|
|
16538
|
+
}), _void(), { kind: "mutation" });
|
|
16345
16539
|
/**
|
|
16346
16540
|
* Zod schemas for persisted record types.
|
|
16347
16541
|
*
|
|
@@ -22708,13 +22902,49 @@ Object.freeze({
|
|
|
22708
22902
|
addonId: null,
|
|
22709
22903
|
access: "create"
|
|
22710
22904
|
},
|
|
22905
|
+
"notificationOutput.deleteTarget": {
|
|
22906
|
+
capName: "notification-output",
|
|
22907
|
+
capScope: "system",
|
|
22908
|
+
addonId: null,
|
|
22909
|
+
access: "delete"
|
|
22910
|
+
},
|
|
22911
|
+
"notificationOutput.discoverTargets": {
|
|
22912
|
+
capName: "notification-output",
|
|
22913
|
+
capScope: "system",
|
|
22914
|
+
addonId: null,
|
|
22915
|
+
access: "view"
|
|
22916
|
+
},
|
|
22917
|
+
"notificationOutput.listTargetKinds": {
|
|
22918
|
+
capName: "notification-output",
|
|
22919
|
+
capScope: "system",
|
|
22920
|
+
addonId: null,
|
|
22921
|
+
access: "view"
|
|
22922
|
+
},
|
|
22923
|
+
"notificationOutput.listTargets": {
|
|
22924
|
+
capName: "notification-output",
|
|
22925
|
+
capScope: "system",
|
|
22926
|
+
addonId: null,
|
|
22927
|
+
access: "view"
|
|
22928
|
+
},
|
|
22711
22929
|
"notificationOutput.send": {
|
|
22712
22930
|
capName: "notification-output",
|
|
22713
22931
|
capScope: "system",
|
|
22714
22932
|
addonId: null,
|
|
22715
22933
|
access: "create"
|
|
22716
22934
|
},
|
|
22717
|
-
"notificationOutput.
|
|
22935
|
+
"notificationOutput.setTargetEnabled": {
|
|
22936
|
+
capName: "notification-output",
|
|
22937
|
+
capScope: "system",
|
|
22938
|
+
addonId: null,
|
|
22939
|
+
access: "create"
|
|
22940
|
+
},
|
|
22941
|
+
"notificationOutput.testTarget": {
|
|
22942
|
+
capName: "notification-output",
|
|
22943
|
+
capScope: "system",
|
|
22944
|
+
addonId: null,
|
|
22945
|
+
access: "create"
|
|
22946
|
+
},
|
|
22947
|
+
"notificationOutput.upsertTarget": {
|
|
22718
22948
|
capName: "notification-output",
|
|
22719
22949
|
capScope: "system",
|
|
22720
22950
|
addonId: null,
|
|
@@ -215198,7 +215428,7 @@ function isNativeObjectForwardingEnabled(capState) {
|
|
|
215198
215428
|
}
|
|
215199
215429
|
//#endregion
|
|
215200
215430
|
//#region src/stream-routing.ts
|
|
215201
|
-
var KIND_RE = /^(native|rtsp|rtmp):(.+)$/;
|
|
215431
|
+
var KIND_RE = /^(native|rtsp|rtmp|flv):(.+)$/;
|
|
215202
215432
|
var CH_PROFILE_RE = /^ch(\d+)-(main|sub|ext)$/;
|
|
215203
215433
|
var PROFILE_ONLY_RE = /^(main|sub|ext)$/;
|
|
215204
215434
|
/**
|
|
@@ -215248,6 +215478,7 @@ function kindLabel(kind) {
|
|
|
215248
215478
|
case "native": return "Native";
|
|
215249
215479
|
case "rtsp": return "RTSP";
|
|
215250
215480
|
case "rtmp": return "RTMP";
|
|
215481
|
+
case "flv": return "FLV";
|
|
215251
215482
|
}
|
|
215252
215483
|
}
|
|
215253
215484
|
/**
|
|
@@ -218694,7 +218925,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
218694
218925
|
*/
|
|
218695
218926
|
resolveAudioCodecApi() {
|
|
218696
218927
|
const router = this.ctx.api.audioCodec;
|
|
218697
|
-
if (!router) throw new Error("Reolink intercom: `audio-codec` capability is not mounted. Install + enable the `addon-audio-codec-
|
|
218928
|
+
if (!router) throw new Error("Reolink intercom: `audio-codec` capability is not mounted. Install + enable the `addon-audio-codec-ffmpeg` addon (or any other addon that provides the `audio-codec` capability) before opening a talk session.");
|
|
218698
218929
|
return {
|
|
218699
218930
|
createDecodeSession: (input) => router.createDecodeSession.mutate(input),
|
|
218700
218931
|
pushEncodedFrame: (input) => router.pushEncodedFrame.mutate(input),
|
|
@@ -219134,6 +219365,23 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
219134
219365
|
}
|
|
219135
219366
|
}
|
|
219136
219367
|
/**
|
|
219368
|
+
* Synthesize the HTTP-FLV pull URL for a (channel, profile) pair. FLV is
|
|
219369
|
+
* served off the Reolink Baichuan media port (default 1935, app `bcs`) as
|
|
219370
|
+
* `channel<channel>_<profile>.bcs` — the same shape Frigate/go2rtc use. The
|
|
219371
|
+
* lib does NOT surface this URL, so we build it from the SAME connection
|
|
219372
|
+
* creds the RTSP/native paths use (`host`/`username`/`password` on the
|
|
219373
|
+
* device config), URL-encoding the credentials.
|
|
219374
|
+
*
|
|
219375
|
+
* Returns `null` when the host isn't locally resolvable (hub-child cameras
|
|
219376
|
+
* borrow the parent's socket and hold no own `host`), so the caller skips
|
|
219377
|
+
* FLV publication for those devices rather than emitting a broken URL.
|
|
219378
|
+
*/
|
|
219379
|
+
buildFlvUrl(channel, profile) {
|
|
219380
|
+
const host = this.config.get("host");
|
|
219381
|
+
if (typeof host !== "string" || host.length === 0) return null;
|
|
219382
|
+
return `http://${host}/flv?port=1935&app=bcs&stream=channel${channel}_${profile}.bcs&user=${encodeURIComponent(String(this.config.get("username") ?? ""))}&password=${encodeURIComponent(String(this.config.get("password") ?? ""))}`;
|
|
219383
|
+
}
|
|
219384
|
+
/**
|
|
219137
219385
|
* Heavy path: talk to the camera and assemble the descriptors. Only ever
|
|
219138
219386
|
* entered when there is no cached catalog (see `buildStreamCatalog`).
|
|
219139
219387
|
*
|
|
@@ -219219,6 +219467,27 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
219219
219467
|
};
|
|
219220
219468
|
enqueueAlt("rtsp", streamOptions.rtspStreams);
|
|
219221
219469
|
enqueueAlt("rtmp", streamOptions.rtmpStreams);
|
|
219470
|
+
for (const s of streamOptions.nativeStreams) {
|
|
219471
|
+
const channel = s.channel ?? (isChild ? ownChannel : 0);
|
|
219472
|
+
const id = buildCamStreamId("flv", channel, s.profile, channelCount);
|
|
219473
|
+
if (desired.has(id)) continue;
|
|
219474
|
+
const flvUrl = this.buildFlvUrl(channel, s.profile);
|
|
219475
|
+
if (!flvUrl) break;
|
|
219476
|
+
const m = s.metadata;
|
|
219477
|
+
desired.set(id, {
|
|
219478
|
+
camStreamId: id,
|
|
219479
|
+
kind: "pull-flv",
|
|
219480
|
+
label: streamLabel(s, "flv"),
|
|
219481
|
+
url: flvUrl,
|
|
219482
|
+
autoEligible: false,
|
|
219483
|
+
...m && m.width > 0 && m.height > 0 ? { resolution: {
|
|
219484
|
+
width: m.width,
|
|
219485
|
+
height: m.height
|
|
219486
|
+
} } : {},
|
|
219487
|
+
...m && m.frameRate > 0 ? { fps: m.frameRate } : {},
|
|
219488
|
+
...normalizeCodecName(m?.videoEncType) ? { codec: normalizeCodecName(m?.videoEncType) } : {}
|
|
219489
|
+
});
|
|
219490
|
+
}
|
|
219222
219491
|
if (desired.size === 0) {
|
|
219223
219492
|
this.ctx.logger.warn("buildStreamCatalog: camera reports no streams — empty catalog", { tags: { deviceId: this.id } });
|
|
219224
219493
|
return [];
|