@apocaliss92/nodelink-js 0.4.11 → 0.4.12
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/BaichuanVideoStream-PHQG4A2L.js +7 -0
- package/dist/{DiagnosticsTools-RNIDFEJK.js → DiagnosticsTools-HGJGVQXZ.js} +3 -2
- package/dist/DiagnosticsTools-HGJGVQXZ.js.map +1 -0
- package/dist/{chunk-EDLMKBG2.js → chunk-IJG45AOT.js} +151 -2733
- package/dist/chunk-IJG45AOT.js.map +1 -0
- package/dist/{chunk-HGQ53FB3.js → chunk-ND73IJIB.js} +765 -45
- package/dist/chunk-ND73IJIB.js.map +1 -0
- package/dist/chunk-W2ANCJVM.js +2703 -0
- package/dist/chunk-W2ANCJVM.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +834 -10
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +3 -2
- package/dist/cli/rtsp-server.js.map +1 -1
- package/dist/index.cjs +1322 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +708 -142
- package/dist/index.d.ts +601 -20
- package/dist/index.js +527 -85
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/dist/chunk-EDLMKBG2.js.map +0 -1
- package/dist/chunk-HGQ53FB3.js.map +0 -1
- /package/dist/{DiagnosticsTools-RNIDFEJK.js.map → BaichuanVideoStream-PHQG4A2L.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/debug/DiagnosticsTools.ts","../src/reolink/baichuan/recordingFileName.ts","../src/reolink/http/ReolinkHttpClient.ts","../src/reolink/cgi/ReolinkCgiApi.ts","../src/debug/zip.ts","../src/rtsp/urls.ts","../src/protocol/xml.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nimport type { ReolinkBaichuanApi } from \"../reolink/baichuan/ReolinkBaichuanApi\";\nimport type { NativeVideoStreamVariant } from \"../reolink/baichuan/types\";\nimport type {\n ReolinkCgiApi,\n DeviceInputData,\n} from \"../reolink/cgi/ReolinkCgiApi\";\nimport type { ReolinkHttpClientOptions } from \"../reolink/http/ReolinkHttpClient\";\nimport { ReolinkCgiApi as ReolinkCgiApiImpl } from \"../reolink/cgi/ReolinkCgiApi\";\nimport { join } from \"node:path\";\nimport { zipDirectory } from \"./zip\";\nimport { BaichuanVideoStream } from \"../baichuan/stream/BaichuanVideoStream\";\nimport type { StreamProfile } from \"../reolink/baichuan/types\";\nimport { buildRtspUrl } from \"../rtsp/urls\";\nimport { splitAnnexBToNalPayloads } from \"../baichuan/stream/H264Converter\";\nimport {\n getH265NalType,\n splitAnnexBToNalPayloads as splitH265AnnexBToNalPayloads,\n} from \"../baichuan/stream/H265Converter\";\nimport type { Logger } from \"./DebugConfig\";\nimport {\n BC_CLASS_MODERN_24,\n BC_CLASS_MODERN_24_ALT,\n BC_CMD_ID_GET_WHITE_LED,\n BC_CMD_ID_VIDEO,\n} from \"../protocol/constants\";\nimport type { BaichuanFrame } from \"../protocol/framing\";\nimport {\n buildChannelExtensionXml,\n buildPreviewStopXml,\n buildPreviewStopXmlV11,\n buildPreviewXml,\n buildPreviewXmlV11,\n} from \"../protocol/xml\";\n\nexport type DiagnosticsStreamKind = \"native\" | \"rtsp\" | \"rtmp\";\n\nexport type DiagnosticsCollectorResult<T> =\n | { ok: true; value: T }\n | { ok: false; error: string };\n\nfunction safeStringifyError(error: unknown): string {\n if (error instanceof Error) return error.stack || error.message;\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n\nasync function tryCall<T>(\n fn: () => Promise<T>,\n): Promise<DiagnosticsCollectorResult<T>> {\n try {\n return { ok: true, value: await fn() };\n } catch (e) {\n return { ok: false, error: safeStringifyError(e) };\n }\n}\n\nfunction mkdirp(dir: string): void {\n fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction writeJson(filePath: string, obj: unknown): void {\n mkdirp(path.dirname(filePath));\n fs.writeFileSync(filePath, JSON.stringify(obj, null, 2));\n}\n\nfunction writeText(filePath: string, text: string): void {\n mkdirp(path.dirname(filePath));\n fs.writeFileSync(filePath, text);\n}\n\nfunction appendNdjson(filePath: string, obj: unknown): void {\n mkdirp(path.dirname(filePath));\n fs.appendFileSync(filePath, JSON.stringify(obj) + \"\\n\");\n}\n\nfunction sleepMs(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction nowIsoCompact(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nfunction nalTypesSummary(\n videoType: \"H264\" | \"H265\",\n accessUnitAnnexB: Buffer,\n): number[] {\n if (videoType === \"H264\") {\n const nals = splitAnnexBToNalPayloads(accessUnitAnnexB);\n return nals.map((n) => (n[0] ?? 0) & 0x1f);\n }\n const nals = splitH265AnnexBToNalPayloads(accessUnitAnnexB);\n return nals\n .map((nal: Buffer) => getH265NalType(nal))\n .filter((t: number | null): t is number => typeof t === \"number\");\n}\n\nfunction normalizeProfiles(\n p: Array<string | undefined | null> | undefined,\n): StreamProfile[] {\n const out: StreamProfile[] = [];\n for (const v of p ?? []) {\n if (v === \"main\" || v === \"sub\" || v === \"ext\") {\n if (!out.includes(v)) out.push(v);\n }\n }\n return out;\n}\n\nexport async function collectNativeDiagnostics(params: {\n api: ReolinkBaichuanApi;\n channel?: number;\n}): Promise<Record<string, unknown>> {\n const { api } = params;\n const channel = params.channel ?? 0;\n\n const result: Record<string, unknown> = {\n kind: \"native\",\n channel,\n transport: api.client.getTransport?.(),\n encKind: api.client.enc?.kind,\n collectedAt: new Date().toISOString(),\n };\n\n const [\n info,\n ports,\n streamMetadata,\n abilities,\n capabilities,\n talkAbility,\n twoWayAudio,\n ] = await Promise.all([\n tryCall(() => api.getInfo(channel)),\n tryCall(() => api.getPorts()),\n tryCall(() => api.getStreamMetadata(channel)),\n tryCall(() => api.getAbilityInfo()),\n tryCall(() => api.getDeviceCapabilities(channel)),\n tryCall(() => api.getTalkAbility(channel)),\n tryCall(() => api.getTwoWayAudioConfig(channel)),\n ]);\n\n result.info = info;\n result.ports = ports;\n result.streamMetadata = streamMetadata;\n result.abilityInfo = abilities;\n result.deviceCapabilities = capabilities;\n result.talkAbility = talkAbility;\n result.twoWayAudio = twoWayAudio;\n\n // Convenience: derive a \"recommended\" config (no side effects) from TalkAbility.\n if (talkAbility?.ok && talkAbility.value) {\n const v: any = talkAbility.value as any;\n const recommended = {\n duplex: Array.isArray(v.duplexList) ? v.duplexList[0] : undefined,\n audioStreamMode: Array.isArray(v.audioStreamModeList)\n ? v.audioStreamModeList[0]\n : undefined,\n audioConfig: Array.isArray(v.audioConfigList)\n ? v.audioConfigList[0]\n : undefined,\n };\n result.intercomRecommended = recommended;\n }\n\n return result;\n}\n\nexport async function collectCgiDiagnostics(params: {\n cgi: ReolinkCgiApi;\n channel?: number;\n}): Promise<Record<string, unknown>> {\n const { cgi } = params;\n const channel = params.channel ?? 0;\n\n const result: Record<string, unknown> = {\n kind: \"cgi\",\n channel,\n collectedAt: new Date().toISOString(),\n };\n\n const [info, netPort, ability, enc, chStatus, chnType, aiState] =\n await Promise.all([\n tryCall(() => cgi.getInfo(channel)),\n tryCall(() => cgi.GetNetPort()),\n tryCall(() => cgi.GetAbility()),\n tryCall(() => cgi.GetEnc(channel)),\n tryCall(() => cgi.GetChannelstatus()),\n tryCall(() => cgi.GetChnTypeInfo(channel)),\n tryCall(() => cgi.GetAiState(channel)),\n ]);\n\n result.info = info;\n result.netPort = netPort;\n result.ability = ability;\n result.enc = enc;\n result.channelStatus = chStatus;\n result.channelTypeInfo = chnType;\n result.aiState = aiState;\n\n // Batch helper includes raw requestBody/response: super useful for user diagnostics.\n result.devicesInfo = await tryCall(() => cgi.getDevicesInfo());\n\n return result;\n}\n\nexport async function createDiagnosticsBundle(params: {\n outDir: string;\n native?: { api: ReolinkBaichuanApi; channel?: number };\n cgi?: { cgi: ReolinkCgiApi; channel?: number };\n extra?: Record<string, unknown>;\n}): Promise<{ outDir: string; diagnosticsPath: string }> {\n const outDir = params.outDir;\n mkdirp(outDir);\n\n const [native, cgi] = await Promise.all([\n params.native\n ? tryCall(() => collectNativeDiagnostics(params.native!))\n : Promise.resolve(undefined),\n params.cgi\n ? tryCall(() => collectCgiDiagnostics(params.cgi!))\n : Promise.resolve(undefined),\n ]);\n\n const diagnostics = {\n createdAt: new Date().toISOString(),\n native,\n cgi,\n ...(params.extra ? { extra: params.extra } : {}),\n };\n\n const diagnosticsPath = path.join(outDir, \"diagnostics.json\");\n writeJson(diagnosticsPath, diagnostics);\n\n return { outDir, diagnosticsPath };\n}\n\nexport type StreamSamplingSelection = {\n kinds: DiagnosticsStreamKind[];\n profiles?: StreamProfile[];\n};\n\nexport type StreamSamplingOptions = {\n outDir: string;\n durationSeconds: number;\n snapshotIntervalSeconds?: number;\n channel?: number;\n selection: StreamSamplingSelection;\n\n /** Optional logger for human-readable progress logs. */\n logger?: Logger;\n\n // RTSP: if enabled, build the direct-camera RTSP URL.\n rtsp?: {\n host: string;\n username: string;\n password: string;\n port?: number;\n };\n\n // RTMP: optional explicit URLs per profile.\n rtmp?: {\n urlsByProfile: Partial<Record<StreamProfile, string>>;\n };\n\n // Native: required when \"native\" is selected.\n native?: {\n api: ReolinkBaichuanApi;\n };\n\n // Limits to avoid runaway dumps.\n limits?: {\n maxNativeRawFrames?: number;\n maxNativeRawBytes?: number;\n };\n};\n\ntype FfmpegResult = { ok: true } | { ok: false; error: string };\n\n/**\n * Sanitize error messages by removing credentials from URLs.\n * Replaces patterns like \"rtsp://username:password@host\" with \"rtsp://***:***@host\"\n */\nfunction sanitizeFfmpegError(error: string): string {\n // Replace credentials in URLs (rtsp://, rtmp://, http://, https://)\n return error.replace(\n /([a-z]+:\\/\\/)([^:@/\\s]+):([^@/\\s]+)@/gi,\n (match, protocol, username, password) => {\n // Keep the protocol, but hide username and password\n return `${protocol}***:***@`;\n },\n );\n}\n\nfunction spawnFfmpeg(args: string[], logPath: string): Promise<FfmpegResult> {\n return new Promise((resolve) => {\n mkdirp(path.dirname(logPath));\n const logStream = fs.createWriteStream(logPath, { flags: \"a\" });\n\n const p = spawn(\"ffmpeg\", args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n p.on(\"error\", (e) => {\n logStream.write(\n `ffmpeg spawn error: ${e instanceof Error ? e.message : String(e)}\\n`,\n );\n logStream.end();\n resolve({ ok: false, error: e instanceof Error ? e.message : String(e) });\n });\n\n let stderr = \"\";\n p.stderr.on(\"data\", (d: Buffer) => {\n const s = d.toString();\n stderr += s;\n // Sanitize credentials before writing to log file\n logStream.write(sanitizeFfmpegError(s));\n });\n\n p.on(\"close\", (code) => {\n logStream.end();\n if (code === 0) {\n resolve({ ok: true });\n return;\n }\n const errorMsg = `ffmpeg exited with code ${code}\\n${stderr.slice(-4000)}`;\n resolve({ ok: false, error: sanitizeFfmpegError(errorMsg) });\n });\n });\n}\n\ntype FfprobeVideoInfo = {\n codecName?: string;\n codecLongName?: string;\n width?: number;\n height?: number;\n avgFrameRate?: string;\n rFrameRate?: string;\n pixFmt?: string;\n};\n\nfunction spawnFfprobeJson(\n args: string[],\n logPath: string,\n): Promise<{ ok: true; json: any } | { ok: false; error: string }> {\n return new Promise((resolve) => {\n mkdirp(path.dirname(logPath));\n const logStream = fs.createWriteStream(logPath, { flags: \"a\" });\n\n const p = spawn(\"ffprobe\", args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n p.on(\"error\", (e) => {\n const msg = e instanceof Error ? e.message : String(e);\n logStream.write(`ffprobe spawn error: ${msg}\\n`);\n logStream.end();\n resolve({ ok: false, error: msg });\n });\n\n let stdout = \"\";\n let stderr = \"\";\n p.stdout.on(\"data\", (d: Buffer) => {\n stdout += d.toString();\n });\n p.stderr.on(\"data\", (d: Buffer) => {\n const s = d.toString();\n stderr += s;\n logStream.write(sanitizeFfmpegError(s));\n });\n\n p.on(\"close\", (code) => {\n logStream.end();\n if (code === 0) {\n try {\n const json = JSON.parse(stdout || \"{}\");\n resolve({ ok: true, json });\n } catch (e) {\n resolve({\n ok: false,\n error: `ffprobe JSON parse failed: ${e instanceof Error ? e.message : String(e)}`,\n });\n }\n return;\n }\n\n resolve({\n ok: false,\n error: sanitizeFfmpegError(\n `ffprobe exited with code ${code}\\n${stderr.slice(-2000)}`,\n ),\n });\n });\n });\n}\n\nasync function probeVideoInfo(params: {\n url: string;\n kind: \"rtsp\" | \"rtmp\";\n logPath: string;\n}): Promise<\n { ok: true; info: FfprobeVideoInfo } | { ok: false; error: string }\n> {\n const args = [\n \"-v\",\n \"error\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-print_format\",\n \"json\",\n \"-show_streams\",\n \"-select_streams\",\n \"v:0\",\n params.url,\n ];\n\n const res = await spawnFfprobeJson(args, params.logPath);\n if (!res.ok) return res;\n\n const streams = Array.isArray(res.json?.streams) ? res.json.streams : [];\n const s0 = streams[0] ?? undefined;\n const info: FfprobeVideoInfo = {\n codecName: typeof s0?.codec_name === \"string\" ? s0.codec_name : undefined,\n codecLongName:\n typeof s0?.codec_long_name === \"string\" ? s0.codec_long_name : undefined,\n width: typeof s0?.width === \"number\" ? s0.width : undefined,\n height: typeof s0?.height === \"number\" ? s0.height : undefined,\n avgFrameRate:\n typeof s0?.avg_frame_rate === \"string\" ? s0.avg_frame_rate : undefined,\n rFrameRate:\n typeof s0?.r_frame_rate === \"string\" ? s0.r_frame_rate : undefined,\n pixFmt: typeof s0?.pix_fmt === \"string\" ? s0.pix_fmt : undefined,\n };\n\n return { ok: true, info };\n}\n\nasync function recordRtspOrRtmpToFile(params: {\n kind: \"rtsp\" | \"rtmp\";\n url: string;\n outputPath: string;\n durationSeconds: number;\n logPath: string;\n}): Promise<FfmpegResult> {\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(params.durationSeconds),\n \"-map\",\n \"0:v:0\",\n \"-map\",\n \"0:a:0?\",\n \"-c\",\n \"copy\",\n \"-y\",\n params.outputPath,\n ];\n\n return await spawnFfmpeg(args, params.logPath);\n}\n\n/**\n * Test stream availability with ffmpeg (short session).\n * Returns success if ffmpeg can connect and receive data.\n */\nasync function testStreamWithFfmpeg(params: {\n url: string;\n kind: \"rtsp\" | \"rtmp\";\n durationSeconds?: number;\n}): Promise<DiagnosticsCollectorResult<{ duration: number }>> {\n const duration = params.durationSeconds ?? 2;\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"error\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(duration),\n \"-f\",\n \"null\",\n \"-\", // Output to null (we just want to test connection)\n ];\n\n return new Promise((resolve) => {\n const p = spawn(\"ffmpeg\", args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n let hasData = false;\n\n p.stderr.on(\"data\", (d: Buffer) => {\n const s = d.toString();\n stderr += s;\n // Check for successful connection indicators\n if (\n s.includes(\"Stream #0\") ||\n s.includes(\"Video:\") ||\n s.includes(\"Audio:\") ||\n s.includes(\"Duration:\")\n ) {\n hasData = true;\n }\n });\n\n p.on(\"close\", (code) => {\n if (code === 0 || hasData) {\n resolve({ ok: true, value: { duration } });\n } else {\n const errorMsg = `ffmpeg exited with code ${code}\\n${stderr.slice(-1000)}`;\n resolve({ ok: false, error: sanitizeFfmpegError(errorMsg) });\n }\n });\n\n p.on(\"error\", (e) => {\n const errorMsg = e instanceof Error ? e.message : String(e);\n resolve({ ok: false, error: sanitizeFfmpegError(errorMsg) });\n });\n });\n}\n\nasync function recordRtspOrRtmp(params: {\n kind: \"rtsp\" | \"rtmp\";\n url: string;\n outputMp4: string;\n durationSeconds: number;\n logPath: string;\n}): Promise<FfmpegResult> {\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(params.durationSeconds),\n \"-c\",\n \"copy\",\n \"-y\",\n params.outputMp4,\n ];\n\n return await spawnFfmpeg(args, params.logPath);\n}\n\nasync function snapshotsRtspOrRtmp(params: {\n kind: \"rtsp\" | \"rtmp\";\n url: string;\n snapshotsPattern: string;\n durationSeconds: number;\n snapshotIntervalSeconds: number;\n logPath: string;\n}): Promise<FfmpegResult> {\n const fpsExpr = `fps=1/${Math.max(0.25, params.snapshotIntervalSeconds)}`;\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n ...(params.kind === \"rtsp\" ? [\"-rtsp_transport\", \"tcp\"] : []),\n \"-i\",\n params.url,\n \"-t\",\n String(params.durationSeconds),\n \"-vf\",\n fpsExpr,\n \"-q:v\",\n \"2\",\n \"-y\",\n params.snapshotsPattern,\n ];\n\n return await spawnFfmpeg(args, params.logPath);\n}\n\nasync function tryJpegFromAnnexB(params: {\n videoType: \"H264\" | \"H265\";\n snapshotAnnexBPath: string;\n outputJpegPath: string;\n logPath: string;\n}): Promise<void> {\n // Best-effort: this is optional for user diagnostics.\n // ffmpeg can decode raw Annex-B elementary streams with -f h264/-f hevc.\n const fmt = params.videoType === \"H265\" ? \"hevc\" : \"h264\";\n const args = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n \"-f\",\n fmt,\n \"-i\",\n params.snapshotAnnexBPath,\n \"-frames:v\",\n \"1\",\n \"-q:v\",\n \"2\",\n \"-y\",\n params.outputJpegPath,\n ];\n\n const res = await spawnFfmpeg(args, params.logPath);\n void res;\n}\n\nexport async function sampleStreams(\n opts: StreamSamplingOptions,\n): Promise<void> {\n const channel = opts.channel ?? 0;\n const durationMs = Math.max(250, Math.round(opts.durationSeconds * 1000));\n const snapshotIntervalSeconds = opts.snapshotIntervalSeconds ?? 2;\n\n const logger = opts.logger;\n const log = (\n level: \"log\" | \"warn\" | \"error\",\n msg: string,\n extra?: unknown,\n ) => {\n const fn = (logger?.[level] ?? logger?.log) as\n | ((...args: any[]) => void)\n | undefined;\n if (!fn) return;\n if (extra !== undefined) fn.call(logger, msg, extra);\n else fn.call(logger, msg);\n };\n\n const profiles = normalizeProfiles(\n opts.selection.profiles,\n ) as StreamProfile[];\n const selectedProfiles: StreamProfile[] = profiles.length\n ? profiles\n : [\"main\", \"sub\", \"ext\"];\n\n const outDir = opts.outDir;\n mkdirp(outDir);\n\n log(\"log\", \"[Diagnostics] stream sampling start\", {\n outDir,\n channel,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n selection: opts.selection,\n });\n\n const eventsPath = path.join(outDir, \"events.ndjson\");\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"sampling_start\",\n channel,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n selection: opts.selection,\n });\n\n for (const profile of selectedProfiles) {\n for (const kind of opts.selection.kinds) {\n const tag = `${kind}_${profile}`;\n const baseDir = path.join(outDir, tag);\n mkdirp(baseDir);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_begin\",\n kind,\n profile,\n channel,\n });\n log(\"log\", \"[Diagnostics] stream begin\", { kind, profile, channel });\n\n if (kind === \"native\") {\n const api = opts.native?.api;\n if (!api) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_skip\",\n kind,\n profile,\n reason: \"native api missing\",\n });\n log(\"warn\", \"[Diagnostics] stream skip (native api missing)\", {\n kind,\n profile,\n channel,\n });\n continue;\n }\n\n const rawDir = path.join(baseDir, \"raw_frames\");\n const snapsDir = path.join(baseDir, \"snapshots\");\n mkdirp(rawDir);\n mkdirp(snapsDir);\n\n const maxRawFrames = opts.limits?.maxNativeRawFrames ?? 400;\n const maxRawBytes = opts.limits?.maxNativeRawBytes ?? 50 * 1024 * 1024;\n let rawFrames = 0;\n let rawBytes = 0;\n\n const expectedStreamType = profile === \"sub\" ? 1 : 0;\n\n const onFrame = (frame: any) => {\n if (!frame?.header) return;\n if (frame.header.cmdId !== 3) return;\n if (frame.header.streamType !== expectedStreamType) return;\n if (rawFrames >= maxRawFrames) return;\n const payload: Buffer =\n Buffer.isBuffer(frame.payload) && frame.payload.length\n ? frame.payload\n : frame.body;\n if (!Buffer.isBuffer(payload) || payload.length === 0) return;\n if (rawBytes + payload.length > maxRawBytes) return;\n\n rawFrames++;\n rawBytes += payload.length;\n\n const idx = String(rawFrames).padStart(6, \"0\");\n const binPath = path.join(rawDir, `frame_${idx}.bin`);\n try {\n fs.writeFileSync(binPath, payload);\n } catch {\n // ignore\n }\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_raw_frame\",\n kind,\n profile,\n idx: rawFrames,\n bytes: payload.length,\n header: frame.header,\n bodyLen: frame.body?.length ?? 0,\n payloadLen: frame.payload?.length ?? 0,\n });\n };\n\n api.client.on(\"frame\", onFrame);\n\n const videoStream = new BaichuanVideoStream({\n client: api.client as any,\n api: api as any,\n channel,\n profile,\n });\n\n const clipBase = path.join(baseDir, `clip_${nowIsoCompact()}`);\n const clipAnnexBPath = clipBase + \".annexb\";\n const clipAudioPath = clipBase + \".audio.bin\";\n const clipInfoPath = clipBase + \".json\";\n\n const videoOut = fs.createWriteStream(clipAnnexBPath, { flags: \"w\" });\n const audioOut = fs.createWriteStream(clipAudioPath, { flags: \"w\" });\n\n let firstVideoType: \"H264\" | \"H265\" | undefined;\n let firstKeyframeAtMs: number | null = null;\n let videoAUs = 0;\n let audioFrames = 0;\n let lastSnapshotAtMs = 0;\n let startedAtMs: number | null = null;\n\n const onAU = (u: any) => {\n if (!u || !Buffer.isBuffer(u.data)) return;\n if (!firstVideoType) firstVideoType = u.videoType;\n\n videoAUs++;\n try {\n videoOut.write(u.data);\n } catch {\n // ignore\n }\n\n if (u.isKeyframe && firstKeyframeAtMs == null) {\n firstKeyframeAtMs = Date.now();\n }\n\n const nalTypes = nalTypesSummary(u.videoType, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_access_unit\",\n kind,\n profile,\n isKeyframe: !!u.isKeyframe,\n videoType: u.videoType,\n microseconds: u.microseconds,\n bytes: u.data.length,\n nalTypes,\n });\n\n const now = Date.now();\n if (\n u.isKeyframe &&\n now - lastSnapshotAtMs >= snapshotIntervalSeconds * 1000\n ) {\n lastSnapshotAtMs = now;\n const snapId = nowIsoCompact();\n const snapAnnex = path.join(\n snapsDir,\n `snap_${snapId}.${u.videoType === \"H265\" ? \"h265\" : \"h264\"}`,\n );\n try {\n fs.writeFileSync(snapAnnex, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_snapshot_saved\",\n kind,\n profile,\n path: snapAnnex,\n });\n\n // Optional: try producing a jpeg (best-effort).\n const snapJpeg = path.join(snapsDir, `snap_${snapId}.jpg`);\n void tryJpegFromAnnexB({\n videoType: u.videoType,\n snapshotAnnexBPath: snapAnnex,\n outputJpegPath: snapJpeg,\n logPath: path.join(baseDir, \"ffmpeg_snapshot.log\"),\n });\n } catch {\n // ignore\n }\n }\n };\n\n const onAudio = (buf: Buffer) => {\n if (!Buffer.isBuffer(buf) || buf.length === 0) return;\n audioFrames++;\n try {\n audioOut.write(buf);\n } catch {\n // ignore\n }\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_audio\",\n kind,\n profile,\n bytes: buf.length,\n });\n };\n\n videoStream.on(\"videoAccessUnit\" as any, onAU as any);\n videoStream.on(\"audioFrame\" as any, onAudio as any);\n videoStream.on(\"error\", (e: any) => {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_error\",\n kind,\n profile,\n error: safeStringifyError(e),\n });\n });\n\n try {\n await videoStream.start();\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_started\",\n kind,\n profile,\n });\n\n startedAtMs = Date.now();\n while (Date.now() - startedAtMs < durationMs) {\n await sleepMs(200);\n }\n } finally {\n try {\n await videoStream.stop();\n } catch {\n // ignore\n }\n\n api.client.removeListener(\"frame\", onFrame);\n\n try {\n videoOut.end();\n } catch {\n // ignore\n }\n try {\n audioOut.end();\n } catch {\n // ignore\n }\n\n const clipInfo = {\n kind,\n profile,\n channel,\n durationSeconds: opts.durationSeconds,\n videoAccessUnits: videoAUs,\n audioFrames,\n videoType: firstVideoType,\n firstKeyframeLatencyMs:\n firstKeyframeAtMs == null || startedAtMs == null\n ? null\n : Math.max(0, firstKeyframeAtMs - startedAtMs),\n rawFrames,\n rawBytes,\n };\n writeJson(clipInfoPath, clipInfo);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_done\",\n ...clipInfo,\n });\n log(\"log\", \"[Diagnostics] stream done\", clipInfo);\n }\n\n continue;\n }\n\n if (kind === \"rtsp\") {\n if (!opts.rtsp) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_skip\",\n kind,\n profile,\n reason: \"rtsp config missing\",\n });\n log(\"warn\", \"[Diagnostics] stream skip (rtsp config missing)\", {\n kind,\n profile,\n channel,\n });\n continue;\n }\n\n const url = buildRtspUrl({\n host: opts.rtsp.host,\n username: opts.rtsp.username,\n password: opts.rtsp.password,\n channel,\n stream: profile,\n ...(opts.rtsp.port != null ? { port: opts.rtsp.port } : {}),\n });\n\n const mp4Path = path.join(baseDir, `clip_${nowIsoCompact()}.mp4`);\n const logPath = path.join(baseDir, \"ffmpeg_record.log\");\n const snapsDir = path.join(baseDir, \"snapshots\");\n mkdirp(snapsDir);\n const snapsPattern = path.join(snapsDir, \"snap_%05d.jpg\");\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtsp_url\",\n kind,\n profile,\n url,\n });\n\n const [recordRes, snapsRes] = await Promise.all([\n recordRtspOrRtmp({\n kind: \"rtsp\",\n url,\n outputMp4: mp4Path,\n durationSeconds: opts.durationSeconds,\n logPath,\n }),\n snapshotsRtspOrRtmp({\n kind: \"rtsp\",\n url,\n snapshotsPattern: snapsPattern,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n logPath: path.join(baseDir, \"ffmpeg_snapshots.log\"),\n }),\n ]);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtsp_done\",\n kind,\n profile,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n log(\"log\", \"[Diagnostics] stream done\", {\n kind,\n profile,\n channel,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n continue;\n }\n\n if (kind === \"rtmp\") {\n const url = opts.rtmp?.urlsByProfile?.[profile];\n if (!url) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"stream_skip\",\n kind,\n profile,\n reason: \"rtmp url missing\",\n });\n log(\"warn\", \"[Diagnostics] stream skip (rtmp url missing)\", {\n kind,\n profile,\n channel,\n });\n continue;\n }\n\n const mp4Path = path.join(baseDir, `clip_${nowIsoCompact()}.mp4`);\n const logPath = path.join(baseDir, \"ffmpeg_record.log\");\n const snapsDir = path.join(baseDir, \"snapshots\");\n mkdirp(snapsDir);\n const snapsPattern = path.join(snapsDir, \"snap_%05d.jpg\");\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtmp_url\",\n kind,\n profile,\n url,\n });\n\n const [recordRes, snapsRes] = await Promise.all([\n recordRtspOrRtmp({\n kind: \"rtmp\",\n url,\n outputMp4: mp4Path,\n durationSeconds: opts.durationSeconds,\n logPath,\n }),\n snapshotsRtspOrRtmp({\n kind: \"rtmp\",\n url,\n snapshotsPattern: snapsPattern,\n durationSeconds: opts.durationSeconds,\n snapshotIntervalSeconds,\n logPath: path.join(baseDir, \"ffmpeg_snapshots.log\"),\n }),\n ]);\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"rtmp_done\",\n kind,\n profile,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n log(\"log\", \"[Diagnostics] stream done\", {\n kind,\n profile,\n channel,\n mp4Path,\n record: recordRes,\n snapshots: snapsRes,\n });\n\n continue;\n }\n }\n }\n\n appendNdjson(eventsPath, { t: Date.now(), type: \"sampling_done\" });\n}\n\n/**\n * Comprehensive NVR/HUB diagnostics function.\n * Collects and prints all available information about the NVR/HUB device and all its channels.\n */\nexport async function collectNvrDiagnostics(params: {\n cgi: ReolinkCgiApi;\n logger?: Logger;\n}): Promise<Record<string, unknown>> {\n const { cgi, logger } = params;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const result: Record<string, unknown> = {\n kind: \"nvr_diagnostics\",\n collectedAt: new Date().toISOString(),\n };\n\n log(\"=\".repeat(80));\n log(\"NVR/HUB DIAGNOSTICS - Starting comprehensive data collection\");\n log(\"=\".repeat(80));\n\n // 1. NVR/HUB Device Information\n log(\"\\n[1/7] Collecting NVR/HUB device information...\");\n const nvrInfo = await tryCall(() => cgi.getNvrInfo());\n result.nvrInfo = nvrInfo;\n if (nvrInfo.ok) {\n log(\"✓ NVR/HUB info collected\", {\n model: nvrInfo.value.devInfo?.type,\n name: nvrInfo.value.devInfo?.name,\n firmware: nvrInfo.value.devInfo?.firmVer,\n hardware: nvrInfo.value.devInfo?.hardVer,\n serial: nvrInfo.value.devInfo?.serial,\n });\n } else {\n log(\"✗ Failed to collect NVR/HUB info\", nvrInfo.error);\n }\n\n // 2. Get all channels\n log(\"\\n[2/7] Discovering channels...\");\n // Use channelNum fallback for multi-focal cameras\n const channelsResult = await tryCall(() =>\n cgi.getChannels({ useChannelNumFallback: true }),\n );\n result.channels = channelsResult;\n let channels: number[] = [];\n if (channelsResult.ok) {\n channels = channelsResult.value.channels;\n log(`✓ Found ${channels.length} channel(s): ${channels.join(\", \")}`);\n result.channelList = channels;\n } else {\n log(\"✗ Failed to discover channels\", channelsResult.error);\n result.channelList = [];\n }\n\n // 3. Devices Information (per-channel: type, AI, encoding)\n log(\"\\n[3/7] Collecting devices information for all channels...\");\n const devicesInfo = await tryCall(() =>\n cgi.getDevicesInfo({ useChannelNumFallback: true }),\n );\n result.devicesInfo = devicesInfo;\n if (devicesInfo.ok) {\n log(\n `✓ Devices info collected for ${Object.keys(devicesInfo.value.devicesData).length} channel(s)`,\n );\n for (const [channel, device] of Object.entries(\n devicesInfo.value.devicesData,\n )) {\n const ch = Number(channel);\n const info = device as any;\n log(` Channel ${ch}:`, {\n model: info.channelInfo?.typeInfo || info.channelStatus?.typeInfo,\n name: info.channelStatus?.name,\n online: info.channelStatus?.online === 1,\n sleeping: info.channelStatus?.sleep === 1,\n uid: info.channelStatus?.uid,\n firmware: info.channelInfo?.firmVer,\n boardInfo: info.channelInfo?.boardInfo,\n });\n }\n } else {\n log(\"✗ Failed to collect devices info\", devicesInfo.error);\n }\n\n // 4. Events and Detection (motion, AI)\n log(\"\\n[4/7] Collecting events and detection states for all channels...\");\n const eventsInfo = await tryCall(() =>\n cgi.getAllChannelsEvents({ useChannelNumFallback: true }),\n );\n result.eventsInfo = eventsInfo;\n if (eventsInfo.ok) {\n log(\n `✓ Events info collected for ${Object.keys(eventsInfo.value.parsed).length} channel(s)`,\n );\n for (const [channel, events] of Object.entries(eventsInfo.value.parsed)) {\n const ch = Number(channel);\n const evt = events as any;\n log(` Channel ${ch}:`, {\n motion: evt.motion,\n detectedObjects: evt.objects,\n });\n }\n } else {\n log(\"✗ Failed to collect events info\", eventsInfo.error);\n }\n\n // 5. Battery Information\n log(\"\\n[5/7] Collecting battery information for all channels...\");\n const batteryInfo = await tryCall(() =>\n cgi.getAllChannelsBatteryInfo({ useChannelNumFallback: true }),\n );\n result.batteryInfo = batteryInfo;\n if (batteryInfo.ok) {\n log(\n `✓ Battery info collected for ${Object.keys(batteryInfo.value.batteryInfoData).length} channel(s)`,\n );\n for (const [channel, battery] of Object.entries(\n batteryInfo.value.batteryInfoData,\n )) {\n const ch = Number(channel);\n const bat = battery as any;\n log(` Channel ${ch}:`, {\n batteryLevel: bat.batteryLevel,\n sleeping: bat.sleeping,\n });\n }\n } else {\n log(\"✗ Failed to collect battery info\", batteryInfo.error);\n }\n\n // 6. Build DeviceInputData map for status info\n log(\"\\n[6/7] Building device capabilities map...\");\n const channelsMap = new Map<number, DeviceInputData>();\n if (devicesInfo.ok && batteryInfo.ok) {\n for (const channel of channels) {\n const device = devicesInfo.value.devicesData[channel];\n const battery = batteryInfo.value.batteryInfoData[channel];\n const abilities = device?.abilities;\n\n channelsMap.set(channel, {\n hasBattery:\n battery?.batteryLevel !== undefined && battery.batteryLevel > 0,\n hasPirEvents: !!(abilities as any)?.pirAlarm,\n hasFloodlight: !!(abilities as any)?.ledCtrl,\n hasPtz: !!(abilities as any)?.ptz,\n sleeping: battery?.sleeping ?? false,\n });\n }\n log(`✓ Built capabilities map for ${channelsMap.size} channel(s)`);\n }\n\n // 7. Status Information (OSD, Floodlight, PIR, PTZ Presets)\n log(\n \"\\n[7/7] Collecting status information (OSD, Floodlight, PIR, PTZ) for all channels...\",\n );\n const statusInfo = await tryCall(() => cgi.getStatusInfo(channelsMap));\n result.statusInfo = statusInfo;\n if (statusInfo.ok) {\n log(\n `✓ Status info collected for ${Object.keys(statusInfo.value.deviceStatusData).length} channel(s)`,\n );\n for (const [channel, status] of Object.entries(\n statusInfo.value.deviceStatusData,\n )) {\n const ch = Number(channel);\n const st = status as any;\n log(` Channel ${ch}:`, {\n osd: st.osd ? \"configured\" : \"not available\",\n floodlightEnabled: st.floodlightEnabled,\n pirEnabled: st.pirEnabled,\n ptzPresets: st.ptzPresets?.length ?? 0,\n });\n }\n } else {\n log(\"✗ Failed to collect status info\", statusInfo.error);\n }\n\n // 8. Additional per-channel details\n log(\"\\n[8/8] Collecting additional per-channel details...\");\n const perChannelDetails: Record<number, Record<string, unknown>> = {};\n\n for (const channel of channels) {\n const channelDetails: Record<string, unknown> = {};\n\n // OSD\n const osd = await tryCall(() => cgi.GetOsd(channel));\n if (osd.ok) channelDetails.osd = osd.value;\n\n // Local Link / WiFi\n const localLink = await tryCall(() => cgi.getLocalLink(channel));\n if (localLink.ok) channelDetails.localLink = localLink.value;\n\n // PIR State\n const pirState = await tryCall(() => cgi.getPirState(channel));\n if (pirState.ok) channelDetails.pirState = pirState.value;\n\n // Siren/Audio Alarm\n const siren = await tryCall(() => cgi.getSiren(channel));\n if (siren.ok) channelDetails.siren = siren.value;\n\n // PTZ Presets\n const ptzPresets = await tryCall(() => cgi.GetPtzPreset(channel));\n if (ptzPresets.ok) channelDetails.ptzPresets = ptzPresets.value;\n\n // White LED / Floodlight\n const whiteLed = await tryCall(() => cgi.GetWhiteLed(channel));\n if (whiteLed.ok) channelDetails.whiteLed = whiteLed.value;\n\n // Encoding Configuration\n const enc = await tryCall(() => cgi.GetEnc(channel));\n if (enc.ok) channelDetails.encoding = enc.value;\n\n // AI State\n const aiState = await tryCall(() => cgi.GetAiState(channel));\n if (aiState.ok) channelDetails.aiState = aiState.value;\n\n // Motion Detection State\n const mdState = await tryCall(() => cgi.GetMdState(channel));\n if (mdState.ok) channelDetails.motionDetection = mdState.value;\n\n // Battery Info\n const battery = await tryCall(() => cgi.GetBatteryInfo(channel));\n if (battery.ok) channelDetails.battery = battery.value;\n\n // Channel Type Info\n const chnType = await tryCall(() => cgi.GetChnTypeInfo(channel));\n if (chnType.ok) channelDetails.channelType = chnType.value;\n\n // WiFi Signal\n const wifiSignal = await tryCall(() => cgi.GetWifiSignal(channel));\n if (wifiSignal.ok) channelDetails.wifiSignal = wifiSignal.value;\n\n perChannelDetails[channel] = channelDetails;\n log(\n ` Channel ${channel}: collected ${Object.keys(channelDetails).length} detail(s)`,\n );\n }\n\n result.perChannelDetails = perChannelDetails;\n log(\n `✓ Additional details collected for ${Object.keys(perChannelDetails).length} channel(s)`,\n );\n\n // Summary\n log(\"\\n\" + \"=\".repeat(80));\n log(\"NVR/HUB DIAGNOSTICS - Summary\");\n log(\"=\".repeat(80));\n log(`Total Channels: ${channels.length}`);\n log(`NVR Info: ${nvrInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Devices Info: ${devicesInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Events Info: ${eventsInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Battery Info: ${batteryInfo.ok ? \"✓\" : \"✗\"}`);\n log(`Status Info: ${statusInfo.ok ? \"✓\" : \"✗\"}`);\n log(\n `Per-Channel Details: ✓ (${Object.keys(perChannelDetails).length} channels)`,\n );\n log(\"=\".repeat(80));\n\n return result;\n}\n\n/**\n * Print NVR/HUB diagnostics in a human-readable format.\n */\nexport function printNvrDiagnostics(\n diagnostics: Record<string, unknown>,\n logger?: Logger,\n): void {\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n log(\"\\n\" + \"=\".repeat(80));\n log(\"NVR/HUB DIAGNOSTICS REPORT\");\n log(\"=\".repeat(80));\n\n // NVR/HUB Info\n const nvrInfo = diagnostics.nvrInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (nvrInfo?.ok) {\n log(\"\\n📡 NVR/HUB DEVICE:\");\n const devInfo = nvrInfo.value.devInfo;\n if (devInfo) {\n log(` Model: ${devInfo.type || \"N/A\"}`);\n log(` Name: ${devInfo.name || \"N/A\"}`);\n log(` Firmware: ${devInfo.firmVer || \"N/A\"}`);\n log(` Hardware: ${devInfo.hardVer || \"N/A\"}`);\n log(` Serial: ${devInfo.serial || \"N/A\"}`);\n log(` Item No: ${devInfo.exactType || devInfo.model || \"N/A\"}`);\n }\n }\n\n // Channels\n const channels = (diagnostics.channelList as number[]) || [];\n log(`\\n📺 CHANNELS (${channels.length}):`);\n\n // Devices Info\n const devicesInfo = diagnostics.devicesInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (devicesInfo?.ok) {\n for (const channel of channels) {\n const device = devicesInfo.value.devicesData?.[channel];\n if (!device) continue;\n\n log(\n `\\n ┌─ Channel ${channel} ────────────────────────────────────────────────────`,\n );\n\n const status = device.channelStatus;\n if (status) {\n log(` │ Status:`);\n log(` │ Online: ${status.online === 1 ? \"✓\" : \"✗\"}`);\n log(` │ Sleeping: ${status.sleep === 1 ? \"Yes\" : \"No\"}`);\n log(` │ UID: ${status.uid || \"N/A\"}`);\n log(` │ Name: ${status.name || \"N/A\"}`);\n }\n\n const channelInfo = device.channelInfo;\n if (channelInfo) {\n log(` │ Device Info:`);\n log(` │ Model: ${channelInfo.typeInfo || \"N/A\"}`);\n log(` │ Firmware: ${channelInfo.firmVer || \"N/A\"}`);\n log(` │ Board Info: ${channelInfo.boardInfo || \"N/A\"}`);\n }\n\n const enc = device.enc;\n if (enc) {\n const encData = enc.Enc;\n log(` │ Encoding:`);\n if (encData) {\n log(\n ` │ Main Stream: ${encData.mainStream?.vType || \"N/A\"} ${encData.mainStream?.vSize || \"\"}`,\n );\n log(\n ` │ Sub Stream: ${encData.subStream?.vType || \"N/A\"} ${encData.subStream?.vSize || \"\"}`,\n );\n }\n }\n\n const ai = device.ai;\n if (ai) {\n log(` │ AI Detection:`);\n const aiKeys = Object.keys(ai).filter((k) => k !== \"channel\");\n if (aiKeys.length > 0) {\n for (const key of aiKeys) {\n const state = (ai as any)[key];\n if (state?.support === 1) {\n log(\n ` │ ${key}: ${state.alarm_state === 1 ? \"Enabled\" : \"Disabled\"}`,\n );\n }\n }\n } else {\n log(` │ No AI detection available`);\n }\n }\n\n const abilities = device.abilities;\n if (abilities) {\n log(` │ Capabilities:`);\n log(` │ Battery: ${(abilities as any).battery ? \"Yes\" : \"No\"}`);\n log(` │ PTZ: ${(abilities as any).ptz ? \"Yes\" : \"No\"}`);\n log(` │ Floodlight: ${(abilities as any).ledCtrl ? \"Yes\" : \"No\"}`);\n log(` │ PIR: ${(abilities as any).pirAlarm ? \"Yes\" : \"No\"}`);\n }\n\n // Events\n const eventsInfo = diagnostics.eventsInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (eventsInfo?.ok) {\n const events = eventsInfo.value.parsed?.[channel];\n if (events) {\n log(` │ Events:`);\n log(` │ Motion: ${events.motion ? \"Yes\" : \"No\"}`);\n log(\n ` │ Detected Objects: ${events.objects?.join(\", \") || \"None\"}`,\n );\n }\n }\n\n // Battery\n const batteryInfo = diagnostics.batteryInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (batteryInfo?.ok) {\n const battery = batteryInfo.value.batteryInfoData?.[channel];\n if (battery) {\n log(` │ Battery:`);\n log(` │ Level: ${battery.batteryLevel}%`);\n log(` │ Sleeping: ${battery.sleeping ? \"Yes\" : \"No\"}`);\n }\n }\n\n // Status\n const statusInfo = diagnostics.statusInfo as\n | DiagnosticsCollectorResult<any>\n | undefined;\n if (statusInfo?.ok) {\n const status = statusInfo.value.deviceStatusData?.[channel];\n if (status) {\n log(` │ Status:`);\n if (status.osd) log(` │ OSD: Configured`);\n if (status.floodlightEnabled !== undefined) {\n log(` │ Floodlight: ${status.floodlightEnabled ? \"On\" : \"Off\"}`);\n }\n if (status.pirEnabled !== undefined) {\n log(` │ PIR: ${status.pirEnabled ? \"Enabled\" : \"Disabled\"}`);\n }\n if (status.ptzPresets) {\n log(` │ PTZ Presets: ${status.ptzPresets.length}`);\n }\n }\n }\n\n // Per-channel details\n const perChannelDetails = diagnostics.perChannelDetails as\n | Record<number, Record<string, unknown>>\n | undefined;\n if (perChannelDetails?.[channel]) {\n const details = perChannelDetails[channel];\n log(` │ Additional Details:`);\n if (details.localLink) {\n const ll = details.localLink as any;\n log(` │ Connection: ${ll.activeLink || \"N/A\"}`);\n log(\n ` │ WiFi Signal: ${ll.wifiSignal !== undefined ? `${ll.wifiSignal}/4` : \"N/A\"}`,\n );\n }\n if (details.siren) {\n const siren = details.siren as any;\n log(` │ Siren: ${siren.enabled ? \"Enabled\" : \"Disabled\"}`);\n }\n }\n\n log(\n ` └────────────────────────────────────────────────────────────────────`,\n );\n }\n }\n\n log(\"\\n\" + \"=\".repeat(80));\n}\n\n/**\n * Test all available streams for a specific channel.\n * Tests RTSP, RTMP, and native Baichuan streams with all profiles (main, sub, ext).\n *\n * @param params - Parameters for stream testing\n * @returns Test results for all stream types and profiles\n */\nexport async function testChannelStreams(params: {\n api: ReolinkBaichuanApi;\n channel: number;\n logger?: Logger;\n}): Promise<Record<string, unknown>> {\n const { api, channel, logger } = params;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const result: Record<string, unknown> = {\n kind: \"channel_stream_test\",\n channel,\n collectedAt: new Date().toISOString(),\n streams: {},\n };\n\n log(\"=\".repeat(80));\n log(`STREAM TEST - Channel ${channel}`);\n log(\"=\".repeat(80));\n\n // Get all available stream options\n log(`\\n[1/3] Getting available stream options for channel ${channel}...`);\n\n // Get raw XML from GetEnc to inspect what streams are actually available\n const encXmlResult = await tryCall(() => api.getEncXml(channel));\n if (encXmlResult.ok) {\n // Check what stream tags are present in the XML\n const xml = encXmlResult.value;\n const tagsPresent = {\n mainStream: /<mainStream\\b/.test(xml),\n subStream: /<subStream\\b/.test(xml),\n extStream: /<extStream\\b/.test(xml),\n thirdStream: /<thirdStream\\b/.test(xml),\n externStream: /<externStream\\b/.test(xml),\n extraStream: /<extraStream\\b/.test(xml),\n };\n log(` Raw GetEnc XML stream tags present: ${JSON.stringify(tagsPresent)}`);\n\n // Log a preview of the XML (first 2000 chars)\n const xmlPreview =\n xml.length > 2000\n ? xml.substring(0, 2000) +\n `\\n... (truncated, total length: ${xml.length})`\n : xml;\n log(` GetEnc XML preview:\\n${xmlPreview}`);\n\n // Also get parsed metadata to see what streams were detected\n const metadataResult = await tryCall(() => api.getStreamMetadata(channel));\n if (metadataResult.ok) {\n const detectedProfiles = metadataResult.value.streams.map(\n (s) => s.profile,\n );\n log(\n ` Parsed streams from metadata: ${detectedProfiles.join(\", \")} (${detectedProfiles.length} total)`,\n );\n result.rawEncXml = xml;\n result.encXmlTagsPresent = tagsPresent;\n result.parsedStreamProfiles = detectedProfiles;\n }\n }\n\n const streamOptionsResult = await tryCall(() =>\n api.buildVideoStreamOptions({ channel }),\n );\n if (!streamOptionsResult.ok) {\n log(`✗ Failed to get stream options: ${streamOptionsResult.error}`);\n result.error = streamOptionsResult.error;\n return result;\n }\n\n const { nativeStreams, rtspStreams, rtmpStreams } = streamOptionsResult.value;\n log(\n `✓ Found ${nativeStreams.length} native, ${rtspStreams.length} RTSP, ${rtmpStreams.length} RTMP stream(s)`,\n );\n\n // Log detailed breakdown of profiles found\n const nativeProfiles = nativeStreams.map((s) => s.profile).join(\", \");\n const rtspProfiles = rtspStreams.map((s) => s.profile).join(\", \");\n const rtmpProfiles = rtmpStreams.map((s) => s.profile).join(\", \");\n log(` Native profiles: [${nativeProfiles || \"none\"}]`);\n log(` RTSP profiles: [${rtspProfiles || \"none\"}]`);\n log(` RTMP profiles: [${rtmpProfiles || \"none\"}]`);\n\n // Test each stream type\n const streamTests: Record<string, Record<string, unknown>> = {};\n\n // Test RTSP streams with ffmpeg\n log(`\\n[2/3] Testing RTSP streams with ffmpeg...`);\n for (const stream of rtspStreams) {\n const key = `rtsp_${stream.profile}`;\n log(` Testing RTSP ${stream.profile}...`);\n\n const testResult: Record<string, unknown> = {\n available: false,\n profile: stream.profile,\n container: stream.container,\n url: stream.url,\n urlWithAuth: stream.urlWithAuth,\n metadata: stream.metadata,\n };\n\n // Test with ffmpeg - short session (2 seconds)\n const testUrl = stream.urlWithAuth;\n const ffmpegTest = await testStreamWithFfmpeg({\n url: testUrl,\n kind: \"rtsp\",\n durationSeconds: 2,\n });\n\n if (ffmpegTest.ok) {\n testResult.available = true;\n testResult.ffmpegSuccess = true;\n if (stream.metadata) {\n testResult.codec = stream.metadata.videoEncType;\n testResult.width = stream.metadata.width;\n testResult.height = stream.metadata.height;\n testResult.fps = stream.metadata.frameRate;\n testResult.bitRate = stream.metadata.bitRate;\n testResult.audio = stream.metadata.audio === 1;\n log(\n ` ✓ Available: ${stream.metadata.width}x${stream.metadata.height} @ ${stream.metadata.frameRate}fps, ${stream.metadata.videoEncType}`,\n );\n } else {\n log(` ✓ Available (ffmpeg test passed)`);\n }\n } else {\n testResult.available = false;\n testResult.error = ffmpegTest.error;\n log(` ✗ Not available: ${ffmpegTest.error}`);\n }\n\n streamTests[key] = testResult;\n }\n\n // Test RTMP streams with ffmpeg\n log(`\\n[3/4] Testing RTMP streams with ffmpeg...`);\n for (const stream of rtmpStreams) {\n const key = `rtmp_${stream.profile}`;\n log(` Testing RTMP ${stream.profile}...`);\n\n const testResult: Record<string, unknown> = {\n available: false,\n profile: stream.profile,\n container: stream.container,\n url: stream.url,\n urlWithAuth: stream.urlWithAuth,\n metadata: stream.metadata,\n };\n\n // Test with ffmpeg - short session (2 seconds)\n const testUrl = stream.urlWithAuth;\n const ffmpegTest = await testStreamWithFfmpeg({\n url: testUrl,\n kind: \"rtmp\",\n durationSeconds: 2,\n });\n\n if (ffmpegTest.ok) {\n testResult.available = true;\n testResult.ffmpegSuccess = true;\n if (stream.metadata) {\n testResult.codec = stream.metadata.videoEncType;\n testResult.width = stream.metadata.width;\n testResult.height = stream.metadata.height;\n testResult.fps = stream.metadata.frameRate;\n testResult.bitRate = stream.metadata.bitRate;\n testResult.audio = stream.metadata.audio === 1;\n log(\n ` ✓ Available: ${stream.metadata.width}x${stream.metadata.height} @ ${stream.metadata.frameRate}fps, ${stream.metadata.videoEncType}`,\n );\n } else {\n log(` ✓ Available (ffmpeg test passed)`);\n }\n } else {\n testResult.available = false;\n testResult.error = ffmpegTest.error;\n log(` ✗ Not available: ${ffmpegTest.error}`);\n }\n\n streamTests[key] = testResult;\n }\n\n // Test native Baichuan streams with short session\n log(`\\n[4/4] Testing native Baichuan streams...`);\n for (const stream of nativeStreams) {\n const variant = (stream.nativeVariant ?? \"default\") as any;\n const key =\n variant === \"default\"\n ? `native_${stream.profile}`\n : `native_${stream.profile}_${variant}`;\n log(\n ` Testing native ${stream.profile}${variant === \"default\" ? \"\" : ` (${variant})`}...`,\n );\n\n const testResult: Record<string, unknown> = {\n available: false,\n profile: stream.profile,\n container: stream.container,\n metadata: stream.metadata,\n };\n\n // Test with short session - subscribe and wait for at least one frame\n try {\n await api.startVideoStream(\n channel,\n stream.profile,\n variant === \"default\" ? undefined : { variant },\n );\n\n // Wait for at least one video frame (max 5 seconds)\n const framePromise = new Promise<boolean>((resolve) => {\n let frameReceived = false;\n const timeout = setTimeout(() => {\n api.client.off(\"push\", onFrame);\n resolve(frameReceived);\n }, 5000);\n\n const onFrame = (frame: BaichuanFrame) => {\n // Check if it's a video frame (cmd_id 3)\n if (frame?.header?.cmdId === BC_CMD_ID_VIDEO) {\n frameReceived = true;\n clearTimeout(timeout);\n api.client.off(\"push\", onFrame);\n resolve(true);\n }\n };\n\n api.client.on(\"push\", onFrame);\n });\n\n const frameReceived = await framePromise;\n\n if (frameReceived) {\n testResult.available = true;\n testResult.frameReceived = true;\n\n if (stream.metadata) {\n testResult.codec = stream.metadata.videoEncType;\n testResult.width = stream.metadata.width;\n testResult.height = stream.metadata.height;\n testResult.fps = stream.metadata.frameRate;\n testResult.bitRate = stream.metadata.bitRate;\n testResult.audio = stream.metadata.audio === 1;\n log(\n ` ✓ Available: ${stream.metadata.width}x${stream.metadata.height} @ ${stream.metadata.frameRate}fps, ${stream.metadata.videoEncType}`,\n );\n } else {\n log(` ✓ Available (frame received)`);\n }\n } else {\n testResult.available = false;\n testResult.error = \"Timeout waiting for video frame\";\n log(` ✗ Not available: timeout waiting for video frame`);\n }\n\n // Stop the stream\n await api.stopVideoStream(\n channel,\n stream.profile,\n variant === \"default\" ? undefined : { variant },\n );\n } catch (error) {\n testResult.available = false;\n testResult.error = safeStringifyError(error);\n log(` ✗ Not available: ${testResult.error}`);\n }\n\n streamTests[key] = testResult;\n }\n\n result.streams = streamTests;\n\n // Summary\n log(\"\\n\" + \"=\".repeat(80));\n log(\"STREAM TEST SUMMARY\");\n log(\"=\".repeat(80));\n const available = Object.values(streamTests).filter(\n (s: any) => s.available === true,\n ).length;\n const total = Object.keys(streamTests).length;\n log(`Available streams: ${available}/${total}`);\n for (const [key, test] of Object.entries(streamTests)) {\n const t = test as any;\n log(\n ` ${key}: ${t.available ? \"✓\" : \"✗\"} ${t.codec || \"N/A\"} ${t.width || \"\"}x${t.height || \"\"}`,\n );\n }\n log(\"=\".repeat(80));\n\n return result;\n}\n\n/**\n * Comprehensive diagnostics for multi-focal devices.\n * Tests all channels and all available streams for each channel.\n *\n * @param params - Parameters for multi-focal diagnostics (see inline types for property descriptions)\n * @returns Complete diagnostics for all channels and streams\n */\nexport async function collectMultifocalDiagnostics(params: {\n /** ReolinkBaichuanApi instance */\n api: ReolinkBaichuanApi;\n /** Optional logger for output */\n logger: Logger;\n}): Promise<Record<string, unknown>> {\n const { api, logger } = params;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const result: Record<string, unknown> = {\n kind: \"multifocal_diagnostics\",\n collectedAt: new Date().toISOString(),\n };\n\n log(\"=\".repeat(80));\n log(\"MULTI-FOCAL DEVICE DIAGNOSTICS - Starting comprehensive analysis\");\n log(\"=\".repeat(80));\n\n // Check if device is multi-focal\n log(\"\\n[1/4] Checking device capabilities...\");\n const capabilitiesResult = await tryCall(() => api.getDeviceCapabilities());\n if (!capabilitiesResult.ok) {\n log(`✗ Failed to get device capabilities: ${capabilitiesResult.error}`);\n result.error = capabilitiesResult.error;\n return result;\n }\n\n const support = capabilitiesResult.value.support;\n const channelNum = support?.channelNum ?? 1;\n\n log(`✓ Device channelNum: ${channelNum}`);\n\n if (channelNum !== 2 && channelNum !== 3) {\n log(\n `⚠ Warning: channelNum is ${channelNum}, expected 2 or 3 for multi-focal device`,\n );\n result.warning = `channelNum is ${channelNum}, expected 2 or 3`;\n }\n\n result.channelNum = channelNum;\n result.deviceInfo = await tryCall(() => api.getInfo());\n\n // Get device info\n log(\"\\n[2/4] Getting device information...\");\n const deviceInfoResult = await tryCall(() => api.getInfo());\n if (deviceInfoResult.ok) {\n log(`✓ Device: ${deviceInfoResult.value.type || \"N/A\"}`);\n result.deviceInfo = deviceInfoResult.value;\n }\n\n // Test all channels\n log(`\\n[3/4] Testing all ${channelNum} channel(s)...`);\n const channelResults: Record<number, Record<string, unknown>> = {};\n\n for (let ch = 0; ch < channelNum; ch++) {\n log(`\\n--- Channel ${ch} ---`);\n const channelTest = await testChannelStreams({\n api,\n channel: ch,\n logger,\n });\n channelResults[ch] = channelTest;\n }\n\n result.channels = channelResults;\n\n // Summary\n log(\"\\n\" + \"=\".repeat(80));\n log(\"MULTI-FOCAL DIAGNOSTICS SUMMARY\");\n log(\"=\".repeat(80));\n log(`Total Channels: ${channelNum}`);\n for (let ch = 0; ch < channelNum; ch++) {\n const channelData = channelResults[ch];\n if (channelData?.streams) {\n const streams = channelData.streams as Record<string, any>;\n const available = Object.values(streams).filter(\n (s: any) => s.available === true,\n ).length;\n const total = Object.keys(streams).length;\n log(` Channel ${ch}: ${available}/${total} streams available`);\n }\n }\n log(\"=\".repeat(80));\n\n return result;\n}\n\nexport interface RunMultifocalDiagnosticsConsecutivelyParams {\n api: ReolinkBaichuanApi;\n /** Base output directory. A timestamped subfolder will be created for each run. */\n outDir: string;\n host: string;\n username: string;\n password: string;\n /** NVR/HUB channel (0-based). */\n channel: number;\n /** Clip length to record per OK stream. */\n durationSeconds: number;\n /** Which RTMP apps to probe. Default: [\"bcs\",\"live\",\"vod\"]. */\n rtmpApps?: Array<\"bcs\" | \"live\" | \"vod\">;\n /** If true, probes a wider set of RTSP/RTMP candidates (may take longer). Default: false */\n probeFull?: boolean;\n /** Treat the target as NVR/Hub channel mapping (0-based). Default: true */\n onNvr?: boolean;\n logger?: Logger;\n}\n\nexport async function runMultifocalDiagnosticsConsecutively(\n params: RunMultifocalDiagnosticsConsecutivelyParams,\n): Promise<{ runDir: string; resultsPath: string; streamsDir: string }> {\n const logger = params.logger;\n const log = (\n level: \"log\" | \"warn\" | \"error\",\n msg: string,\n extra?: unknown,\n ) => {\n const fn = (logger?.[level] ?? logger?.log) as\n | ((...args: any[]) => void)\n | undefined;\n if (fn) {\n if (extra !== undefined) fn.call(logger, msg, extra);\n else fn.call(logger, msg);\n return;\n }\n\n // Fallback for non-logger callers.\n // Keep output minimal but informative.\n if (extra !== undefined) console.log(msg, extra);\n else console.log(msg);\n };\n\n // NOTE: user requested diagnostics to be written directly in the provided root folder.\n // We still keep `streams/` and `logs/` subfolders inside that root.\n const runDir = params.outDir;\n const streamsDir = join(runDir, \"streams\");\n const logsDir = join(runDir, \"logs\");\n mkdirp(runDir);\n mkdirp(streamsDir);\n mkdirp(logsDir);\n\n const redact = (s: string) =>\n s\n .replaceAll(encodeURIComponent(params.password), \"***\")\n .replaceAll(params.password, \"***\");\n\n log(\"log\", \"[MultifocalDiagnostics] starting run\", {\n outDir: params.outDir,\n runDir,\n host: params.host,\n channel: params.channel,\n durationSeconds: params.durationSeconds,\n rtmpApps: params.rtmpApps ?? [\"bcs\", \"live\", \"vod\"],\n probeFull: params.probeFull === true,\n onNvr: params.onNvr !== false,\n });\n\n const results: any = {\n kind: \"multifocal_diagnostics_run\",\n collectedAt: new Date().toISOString(),\n host: params.host,\n channel: params.channel,\n durationSeconds: params.durationSeconds,\n ok: [] as any[],\n failed: [] as any[],\n };\n\n const channelStr2 = String(params.channel + 1).padStart(2, \"0\");\n const userEnc = encodeURIComponent(params.username);\n const passEnc = encodeURIComponent(params.password);\n\n const rtspCandidates = (() => {\n const base = [\n `/Preview_${channelStr2}_main`,\n `/Preview_${channelStr2}_sub`,\n `/Preview_${channelStr2}_autotrack`,\n `/h264Preview_${channelStr2}_main`,\n `/h264Preview_${channelStr2}_sub`,\n `/h264Preview_${channelStr2}_autotrack`,\n `/h265Preview_${channelStr2}_main`,\n `/h265Preview_${channelStr2}_sub`,\n `/h265Preview_${channelStr2}_autotrack`,\n ];\n\n if (params.probeFull) {\n base.unshift(\n `/rtsp/Preview_${channelStr2}_main`,\n `/rtsp/Preview_${channelStr2}_sub`,\n `/rtsp/Preview_${channelStr2}_mobile`,\n `/rtsp/Preview_${channelStr2}_autotrack`,\n );\n base.push(\n `/Preview_${channelStr2}_mobile`,\n `/Preview_${channelStr2}_autotrack_main`,\n `/Preview_${channelStr2}_autotrack_sub`,\n );\n }\n\n return [...new Set(base)];\n })();\n\n const rtmpApps = params.rtmpApps ?? [\"bcs\", \"live\", \"vod\"];\n const rtmpCandidates = (() => {\n const minimalStreams = [\"sub\", \"mobile\", \"autotrack_sub\", \"telephoto_sub\"];\n const fullStreams = [\n \"main\",\n \"sub\",\n \"mobile\",\n \"autotrack\",\n \"autotrack_main\",\n \"autotrack_sub\",\n \"telephoto_main\",\n \"telephoto_sub\",\n ];\n const streams = params.probeFull ? fullStreams : minimalStreams;\n const out: Array<{ app: string; streamName: string; url: string }> = [];\n for (const app of rtmpApps) {\n for (const streamName of streams) {\n const streamType =\n streamName.includes(\"sub\") ||\n streamName === \"sub\" ||\n streamName === \"mobile\"\n ? 1\n : 0;\n const path = `/${app}/channel${params.channel}_${streamName}.bcs`;\n const u = new URL(`rtmp://${params.host}:1935${path}`);\n u.searchParams.set(\"channel\", params.channel.toString());\n u.searchParams.set(\"stream\", streamType.toString());\n u.searchParams.set(\"user\", params.username);\n u.searchParams.set(\"password\", params.password);\n out.push({ app, streamName, url: u.toString() });\n }\n }\n return out;\n })();\n\n const okLine = (entry: any) => {\n const wh =\n entry.width && entry.height ? `${entry.width}x${entry.height}` : \"?\";\n const codec = entry.codec ?? entry.codecName ?? \"\";\n return `${entry.kind} ${entry.id} ${wh} ${codec}`.trim();\n };\n\n log(\"log\", \"[MultifocalDiagnostics] probing RTSP\", {\n candidates: rtspCandidates.length,\n });\n for (const pathCandidate of rtspCandidates) {\n const urlWithAuth = `rtsp://${userEnc}:${passEnc}@${params.host}:554${pathCandidate}`;\n const id = `rtsp:${pathCandidate}`;\n const baseName = `${nowIsoCompact()}_${id}`.replace(\n /[^a-zA-Z0-9._-]+/g,\n \"_\",\n );\n const outPath = join(streamsDir, `${baseName}.mkv`);\n const probeLog = join(logsDir, `${baseName}.ffprobe.log`);\n const recLog = join(logsDir, `${baseName}.ffmpeg.log`);\n\n const probe = await probeVideoInfo({\n url: urlWithAuth,\n kind: \"rtsp\",\n logPath: probeLog,\n });\n if (!probe.ok) {\n results.failed.push({\n kind: \"rtsp\",\n id,\n url: redact(urlWithAuth),\n error: probe.error,\n });\n continue;\n }\n\n const rec = await recordRtspOrRtmpToFile({\n kind: \"rtsp\",\n url: urlWithAuth,\n outputPath: outPath,\n durationSeconds: params.durationSeconds,\n logPath: recLog,\n });\n if (!rec.ok) {\n results.failed.push({\n kind: \"rtsp\",\n id,\n url: redact(urlWithAuth),\n error: rec.error,\n });\n continue;\n }\n\n const entry = {\n kind: \"rtsp\",\n id: pathCandidate,\n url: redact(urlWithAuth),\n clipPath: outPath,\n ...probe.info,\n };\n results.ok.push(entry);\n log(\"log\", \"[MultifocalDiagnostics] clip saved\", {\n kind: \"rtsp\",\n id: pathCandidate,\n clipPath: outPath,\n width: probe.info.width,\n height: probe.info.height,\n codec: probe.info.codecName,\n });\n log(\"log\", `[MultifocalDiagnostics] OK ${okLine(entry)}`);\n }\n\n log(\"log\", \"[MultifocalDiagnostics] probing RTMP\", {\n candidates: rtmpCandidates.length,\n });\n for (const cand of rtmpCandidates) {\n const id = `rtmp:${cand.app}:${cand.streamName}`;\n const baseName = `${nowIsoCompact()}_${id}`.replace(\n /[^a-zA-Z0-9._-]+/g,\n \"_\",\n );\n const outPath = join(streamsDir, `${baseName}.mkv`);\n const probeLog = join(logsDir, `${baseName}.ffprobe.log`);\n const recLog = join(logsDir, `${baseName}.ffmpeg.log`);\n\n const probe = await probeVideoInfo({\n url: cand.url,\n kind: \"rtmp\",\n logPath: probeLog,\n });\n if (!probe.ok) {\n results.failed.push({\n kind: \"rtmp\",\n id,\n url: redact(cand.url),\n error: probe.error,\n });\n continue;\n }\n\n const rec = await recordRtspOrRtmpToFile({\n kind: \"rtmp\",\n url: cand.url,\n outputPath: outPath,\n durationSeconds: params.durationSeconds,\n logPath: recLog,\n });\n if (!rec.ok) {\n results.failed.push({\n kind: \"rtmp\",\n id,\n url: redact(cand.url),\n error: rec.error,\n });\n continue;\n }\n\n const entry = {\n kind: \"rtmp\",\n id,\n url: redact(cand.url),\n clipPath: outPath,\n app: cand.app,\n streamName: cand.streamName,\n ...probe.info,\n };\n results.ok.push(entry);\n log(\"log\", \"[MultifocalDiagnostics] clip saved\", {\n kind: \"rtmp\",\n id,\n clipPath: outPath,\n width: probe.info.width,\n height: probe.info.height,\n codec: probe.info.codecName,\n });\n log(\"log\", `[MultifocalDiagnostics] OK ${okLine(entry)}`);\n }\n\n // Native probing: do NOT rely solely on buildVideoStreamOptions.\n // Probe a matrix of (mode nvr/standalone, channel, profile, variant) and save rich artifacts.\n log(\"log\", \"[MultifocalDiagnostics] probing native streams\", {\n channel: params.channel,\n onNvr: params.onNvr !== false,\n probeFull: params.probeFull === true,\n });\n\n // Keep buildVideoStreamOptions output for reference (debugging what the API thinks).\n results.nativeStreamOptions = await tryCall(() =>\n params.api.buildVideoStreamOptions({\n channel: params.channel,\n onNvr: params.onNvr !== false,\n }),\n );\n\n const nativeModes: Array<\"nvr\" | \"standalone\"> =\n params.onNvr === false && !params.probeFull\n ? [\"standalone\"]\n : [\"nvr\", \"standalone\"];\n\n const uniqNums = (arr: number[]) =>\n [...new Set(arr)].filter((n) => Number.isFinite(n) && n >= 0);\n const channelsForMode = (mode: \"nvr\" | \"standalone\"): number[] => {\n if (mode === \"nvr\") return [params.channel];\n // Standalone TrackMix usually uses channels 0 (wide) and 1 (tele). Keep current channel if it matches.\n return uniqNums([0, 1, params.channel].filter((n) => n === 0 || n === 1));\n };\n\n const nativeProfiles: StreamProfile[] = params.probeFull\n ? [\"main\", \"sub\", \"ext\"]\n : [\"main\", \"sub\"];\n const nativeVariants: NativeVideoStreamVariant[] = params.probeFull\n ? [\"default\", \"autotrack\", \"telephoto\"]\n : [\"default\"];\n\n const expectedStreamTypesFor = (\n profile: StreamProfile,\n variant: NativeVideoStreamVariant,\n ): Set<number> => {\n if (profile === \"sub\") {\n return new Set([variant === \"default\" ? 1 : 3]);\n }\n if (profile === \"main\") {\n return new Set([variant === \"default\" ? 0 : 2]);\n }\n // ext\n return new Set([0]);\n };\n\n for (const mode of nativeModes) {\n for (const chNative of channelsForMode(mode)) {\n for (const profile of nativeProfiles) {\n for (const variant of nativeVariants) {\n if (profile === \"ext\" && variant !== \"default\") {\n results.failed.push({\n kind: \"native\",\n id: `native:${mode}:ch${chNative}:${profile}:${variant}`,\n error: \"invalid (ext does not support variant)\",\n });\n continue;\n }\n\n const id = `native:${mode}:ch${chNative}:${profile}:${variant}`;\n const baseName = `${nowIsoCompact()}_${id}`.replace(\n /[^a-zA-Z0-9._-]+/g,\n \"_\",\n );\n const baseDir = join(\n streamsDir,\n \"native\",\n mode,\n `ch${chNative}`,\n profile,\n variant,\n );\n const rawDir = join(baseDir, \"raw_frames\");\n const snapsDir = join(baseDir, \"snapshots\");\n const logsBase = join(logsDir, baseName);\n mkdirp(rawDir);\n mkdirp(snapsDir);\n\n const eventsPath = join(baseDir, \"events.ndjson\");\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_begin\",\n id,\n mode,\n channel: chNative,\n profile,\n variant,\n });\n log(\"log\", \"[MultifocalDiagnostics] native begin\", { id });\n\n const expectedStreamTypes = expectedStreamTypesFor(profile, variant);\n const maxRawFrames = 400;\n const maxRawBytes = 50 * 1024 * 1024;\n let rawFrames = 0;\n let rawBytes = 0;\n let lockedChannelId: number | undefined;\n let lockedMsgNum: number | undefined;\n\n const onPush = (frame: BaichuanFrame) => {\n if (!frame?.header) return;\n if (frame.header.cmdId !== 3) return;\n if (!expectedStreamTypes.has(frame.header.streamType)) return;\n\n // Lock to first observed channelId/msgNum for this combination.\n if (lockedChannelId === undefined)\n lockedChannelId = frame.header.channelId;\n if (lockedMsgNum === undefined) lockedMsgNum = frame.header.msgNum;\n if (frame.header.channelId !== lockedChannelId) return;\n if (frame.header.msgNum !== lockedMsgNum) return;\n\n if (rawFrames >= maxRawFrames) return;\n const payload: Buffer =\n Buffer.isBuffer(frame.payload) && frame.payload.length\n ? frame.payload\n : frame.body;\n if (!Buffer.isBuffer(payload) || payload.length === 0) return;\n if (rawBytes + payload.length > maxRawBytes) return;\n\n rawFrames++;\n rawBytes += payload.length;\n const idx = String(rawFrames).padStart(6, \"0\");\n const binPath = join(rawDir, `frame_${idx}.bin`);\n try {\n fs.writeFileSync(binPath, payload);\n } catch {\n // ignore\n }\n\n if (rawFrames === 1) {\n try {\n writeJson(\n join(baseDir, \"first_frame_header.json\"),\n frame.header,\n );\n } catch {\n // ignore\n }\n }\n\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_raw_frame\",\n id,\n bytes: payload.length,\n header: frame.header,\n bodyLen: frame.body?.length ?? 0,\n payloadLen: frame.payload?.length ?? 0,\n });\n };\n\n const clipBase = join(baseDir, `clip_${nowIsoCompact()}`);\n const clipAnnexBPath = clipBase + \".annexb\";\n const clipAudioPath = clipBase + \".audio.bin\";\n const clipInfoPath = clipBase + \".json\";\n const ffmpegMuxLog = logsBase + \".ffmpeg_mux.log\";\n\n const videoOut = fs.createWriteStream(clipAnnexBPath, { flags: \"w\" });\n const audioOut = fs.createWriteStream(clipAudioPath, { flags: \"w\" });\n\n let firstVideoType: \"H264\" | \"H265\" | undefined;\n let firstKeyframeAtMs: number | null = null;\n let startedAtMs: number | null = null;\n let videoAUs = 0;\n let audioFrames = 0;\n let lastSnapshotAtMs = 0;\n\n const client: any = params.api.client as any;\n\n const videoStream = new BaichuanVideoStream({\n client,\n // Intentionally omit `api`: we will manually issue start/stop VIDEO requests\n // to probe different streamType + Preview XML combinations.\n channel: chNative,\n profile,\n ...(variant !== \"default\" ? { variant } : {}),\n ...(logger ? { logger } : {}),\n } as any);\n\n const onAU = (u: any) => {\n if (!u || !Buffer.isBuffer(u.data)) return;\n if (!firstVideoType) firstVideoType = u.videoType;\n videoAUs++;\n try {\n videoOut.write(u.data);\n } catch {\n // ignore\n }\n\n if (u.isKeyframe && firstKeyframeAtMs == null)\n firstKeyframeAtMs = Date.now();\n\n const nalTypes = nalTypesSummary(u.videoType, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_access_unit\",\n id,\n isKeyframe: !!u.isKeyframe,\n videoType: u.videoType,\n microseconds: u.microseconds,\n bytes: u.data.length,\n nalTypes,\n });\n\n const now = Date.now();\n if (u.isKeyframe && now - lastSnapshotAtMs >= 2_000) {\n lastSnapshotAtMs = now;\n const snapId = nowIsoCompact();\n const snapAnnex = join(\n snapsDir,\n `snap_${snapId}.${u.videoType === \"H265\" ? \"h265\" : \"h264\"}`,\n );\n try {\n fs.writeFileSync(snapAnnex, u.data);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_snapshot_saved\",\n id,\n path: snapAnnex,\n });\n const snapJpeg = join(snapsDir, `snap_${snapId}.jpg`);\n void tryJpegFromAnnexB({\n videoType: u.videoType,\n snapshotAnnexBPath: snapAnnex,\n outputJpegPath: snapJpeg,\n logPath: join(baseDir, \"ffmpeg_snapshot.log\"),\n });\n } catch {\n // ignore\n }\n }\n };\n\n const onAudio = (buf: Buffer) => {\n if (!Buffer.isBuffer(buf) || buf.length === 0) return;\n audioFrames++;\n try {\n audioOut.write(buf);\n } catch {\n // ignore\n }\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_audio\",\n id,\n bytes: buf.length,\n });\n };\n\n videoStream.on(\"videoAccessUnit\" as any, onAU as any);\n videoStream.on(\"audioFrame\" as any, onAudio as any);\n videoStream.on(\"error\", (e: any) => {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_error\",\n id,\n error: safeStringifyError(e),\n });\n log(\"warn\", \"[MultifocalDiagnostics] native stream error\", {\n id,\n error: safeStringifyError(e),\n });\n });\n\n const uniq = <T>(arr: T[]) => [...new Set(arr)];\n\n const baseStreamName =\n profile === \"main\"\n ? \"mainStream\"\n : profile === \"sub\"\n ? \"subStream\"\n : \"externStream\";\n const headerStreamTypeCandidates =\n profile === \"sub\" ? [1, 3] : profile === \"main\" ? [0, 2] : [0];\n\n // Candidate channelId tags (some firmwares use 0-based, others 1-based).\n const channelIdTagCandidates = uniq(\n [chNative, chNative + 1].filter(\n (n) => Number.isFinite(n) && n >= 0,\n ),\n );\n\n const messageClassCandidates = params.probeFull\n ? [BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT]\n : [BC_CLASS_MODERN_24];\n const extensionXmlCandidates = params.probeFull\n ? [\"\", buildChannelExtensionXml(chNative)]\n : [buildChannelExtensionXml(chNative)];\n\n type NativeStartAttempt = {\n attemptId: string;\n messageClass: number;\n streamTypeHeader: number;\n payloadXml: string;\n payloadVersion: \"v10\" | \"v11\";\n channelIdTag: number | undefined;\n handle: number;\n previewStreamType: string;\n extensionXml: string;\n };\n\n const attempts: NativeStartAttempt[] = [];\n\n for (const messageClass of messageClassCandidates) {\n for (const streamTypeHeader of headerStreamTypeCandidates) {\n for (const extensionXml of extensionXmlCandidates) {\n // Preview v1.0: try both with and without channelId.\n const v10ChannelIdTags = params.probeFull\n ? [undefined, ...channelIdTagCandidates]\n : [undefined, chNative];\n for (const channelIdTag of uniq(v10ChannelIdTags)) {\n const handle =\n profile === \"main\" ? 0 : profile === \"sub\" ? 256 : 1024;\n attempts.push({\n attemptId: `v10:${baseStreamName}:h${handle}:cid${channelIdTag ?? \"none\"}:st${streamTypeHeader}:ext${extensionXml ? \"1\" : \"0\"}:mc${messageClass}`,\n messageClass,\n streamTypeHeader,\n payloadXml: buildPreviewXml(\n handle,\n baseStreamName,\n channelIdTag,\n ),\n payloadVersion: \"v10\",\n channelIdTag,\n handle,\n previewStreamType: baseStreamName,\n extensionXml,\n });\n }\n\n // Preview v1.1: always includes channelId; also try tele PCAP variants.\n for (const channelIdTag of channelIdTagCandidates) {\n const handle =\n profile === \"main\" ? 0 : profile === \"sub\" ? 256 : 1024;\n attempts.push({\n attemptId: `v11:${baseStreamName}:h${handle}:cid${channelIdTag}:st${streamTypeHeader}:ext${extensionXml ? \"1\" : \"0\"}:mc${messageClass}`,\n messageClass,\n streamTypeHeader,\n payloadXml: buildPreviewXmlV11({\n channelId: channelIdTag,\n handle,\n streamType: baseStreamName,\n }),\n payloadVersion: \"v11\",\n channelIdTag,\n handle,\n previewStreamType: baseStreamName,\n extensionXml,\n });\n\n if (variant === \"telephoto\") {\n const telePreviewStreamType =\n profile === \"main\"\n ? \"externStream\"\n : profile === \"sub\"\n ? \"mobileStream\"\n : undefined;\n const teleHandleBase =\n profile === \"main\"\n ? 1024\n : profile === \"sub\"\n ? 512\n : undefined;\n if (telePreviewStreamType && teleHandleBase !== undefined) {\n const teleHandle = teleHandleBase + channelIdTag;\n // Empirically: Hub tele often requires header streamType=0.\n attempts.push({\n attemptId: `v11tele:${telePreviewStreamType}:h${teleHandle}:cid${channelIdTag}:st0:ext${extensionXml ? \"1\" : \"0\"}:mc${messageClass}`,\n messageClass,\n streamTypeHeader: 0,\n payloadXml: buildPreviewXmlV11({\n channelId: channelIdTag,\n handle: teleHandle,\n streamType: telePreviewStreamType,\n }),\n payloadVersion: \"v11\",\n channelIdTag,\n handle: teleHandle,\n previewStreamType: telePreviewStreamType,\n extensionXml,\n });\n }\n }\n }\n }\n }\n }\n\n // Capture raw frames (cmd_id=3) concurrently.\n client.on(\"push\", onPush);\n\n let lastAttemptOk: NativeStartAttempt | undefined;\n let lastStartMsgNum: number | undefined;\n\n try {\n for (const attempt of attempts) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_begin\",\n id,\n attemptId: attempt.attemptId,\n });\n videoAUs = 0;\n audioFrames = 0;\n firstVideoType = undefined;\n firstKeyframeAtMs = null;\n startedAtMs = null;\n lockedChannelId = undefined;\n lockedMsgNum = undefined;\n rawFrames = 0;\n rawBytes = 0;\n\n const msgNum = client.reserveNextMsgNum();\n lastStartMsgNum = msgNum;\n try {\n client.subscribeVideoStream(BC_CMD_ID_VIDEO, msgNum);\n } catch {\n // ignore\n }\n\n // Force msgNum filtering in BaichuanVideoStream.\n try {\n (videoStream as any).activeMsgNum = msgNum;\n } catch {\n // ignore\n }\n\n try {\n await videoStream.start();\n const resp = await client.sendFrame({\n cmdId: BC_CMD_ID_VIDEO,\n channel: chNative,\n channelIdOverride: chNative,\n msgNumOverride: msgNum,\n extensionXml: attempt.extensionXml,\n payloadXml: attempt.payloadXml,\n messageClass: attempt.messageClass,\n streamType: attempt.streamTypeHeader,\n timeoutMs: 20_000,\n });\n\n if (resp?.header?.responseCode !== 200) {\n throw new Error(\n `response_code ${resp?.header?.responseCode}`,\n );\n }\n\n startedAtMs = Date.now();\n while (\n Date.now() - startedAtMs <\n Math.max(250, Math.round(params.durationSeconds * 1000))\n ) {\n await sleepMs(200);\n }\n\n // Consider it OK only if we got at least some media.\n if (videoAUs > 0) {\n lastAttemptOk = attempt;\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_ok\",\n id,\n attemptId: attempt.attemptId,\n msgNum,\n });\n break;\n }\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_no_media\",\n id,\n attemptId: attempt.attemptId,\n msgNum,\n });\n } catch (e) {\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_attempt_failed\",\n id,\n attemptId: attempt.attemptId,\n error: safeStringifyError(e),\n });\n } finally {\n try {\n // Best-effort stop: some firmwares keep streaming unless a stop is sent.\n const stopXml =\n attempt.payloadVersion === \"v11\" &&\n attempt.channelIdTag !== undefined\n ? buildPreviewStopXmlV11({\n channelId: attempt.channelIdTag,\n handle: attempt.handle,\n })\n : buildPreviewStopXml(\n attempt.handle,\n attempt.channelIdTag,\n );\n await client.sendFrame({\n cmdId: BC_CMD_ID_VIDEO,\n channel: chNative,\n channelIdOverride: chNative,\n msgNumOverride: msgNum,\n extensionXml: attempt.extensionXml,\n payloadXml: stopXml,\n messageClass: attempt.messageClass,\n streamType: attempt.streamTypeHeader,\n timeoutMs: 5_000,\n });\n } catch {\n // ignore\n }\n try {\n client.unsubscribeVideoStream(BC_CMD_ID_VIDEO, msgNum);\n } catch {\n // ignore\n }\n try {\n await videoStream.stop();\n } catch {\n // ignore\n }\n }\n }\n\n if (!lastAttemptOk) {\n throw new Error(\"no working native start attempt produced media\");\n }\n } catch (e) {\n results.failed.push({\n kind: \"native\",\n id,\n error: safeStringifyError(e),\n });\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_failed\",\n id,\n error: safeStringifyError(e),\n });\n continue;\n } finally {\n client.removeListener(\"push\", onPush);\n try {\n videoOut.end();\n } catch {\n // ignore\n }\n try {\n audioOut.end();\n } catch {\n // ignore\n }\n }\n\n const mkvPath = clipBase + \".mkv\";\n if (firstVideoType) {\n const fmt = firstVideoType === \"H265\" ? \"hevc\" : \"h264\";\n const muxArgs = [\n \"-hide_banner\",\n \"-loglevel\",\n \"warning\",\n \"-stats\",\n \"-f\",\n fmt,\n \"-i\",\n clipAnnexBPath,\n \"-c\",\n \"copy\",\n \"-y\",\n mkvPath,\n ];\n const muxRes = await spawnFfmpeg(muxArgs, ffmpegMuxLog);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_mux\",\n id,\n ok: muxRes.ok,\n });\n }\n\n const info: any = {\n kind: \"native\",\n id,\n mode,\n channel: chNative,\n profile,\n variant,\n durationSeconds: params.durationSeconds,\n nativeAttempt: lastAttemptOk,\n nativeMsgNum: lastStartMsgNum,\n videoAccessUnits: videoAUs,\n audioFrames,\n videoType: firstVideoType,\n firstKeyframeLatencyMs:\n firstKeyframeAtMs == null || startedAtMs == null\n ? null\n : Math.max(0, firstKeyframeAtMs - startedAtMs),\n rawFrames,\n rawBytes,\n lockedChannelId,\n lockedMsgNum,\n annexbPath: clipAnnexBPath,\n audioPath: clipAudioPath,\n mkvPath: fs.existsSync(mkvPath) ? mkvPath : undefined,\n };\n\n // Derive resolution/codec from the saved MKV if available.\n if (info.mkvPath) {\n const p = await spawnFfprobeJson(\n [\n \"-v\",\n \"error\",\n \"-print_format\",\n \"json\",\n \"-show_streams\",\n \"-select_streams\",\n \"v:0\",\n info.mkvPath,\n ],\n logsBase + \".ffprobe_file.log\",\n );\n if (p.ok) {\n const streams = Array.isArray(p.json?.streams)\n ? p.json.streams\n : [];\n const s0 = streams[0] ?? undefined;\n info.width = typeof s0?.width === \"number\" ? s0.width : undefined;\n info.height =\n typeof s0?.height === \"number\" ? s0.height : undefined;\n info.codecName =\n typeof s0?.codec_name === \"string\" ? s0.codec_name : undefined;\n }\n }\n\n writeJson(clipInfoPath, info);\n appendNdjson(eventsPath, {\n t: Date.now(),\n type: \"native_done\",\n ...info,\n });\n\n const entry = {\n kind: \"native\",\n id,\n clipPath: info.mkvPath ?? clipAnnexBPath,\n width: info.width,\n height: info.height,\n codec:\n info.codecName ??\n (firstVideoType === \"H265\"\n ? \"hevc\"\n : firstVideoType === \"H264\"\n ? \"h264\"\n : undefined),\n profile,\n nativeVariant: variant,\n mode,\n channel: chNative,\n };\n results.ok.push(entry);\n log(\"log\", \"[MultifocalDiagnostics] clip saved\", {\n kind: \"native\",\n id,\n clipPath: entry.clipPath,\n width: entry.width,\n height: entry.height,\n codec: entry.codec,\n rawFrames,\n rawBytes,\n videoAccessUnits: videoAUs,\n });\n log(\"log\", `[MultifocalDiagnostics] OK ${okLine(entry)}`);\n }\n }\n }\n }\n\n const okIds = (results.ok as any[]).map((x) => ({\n kind: x.kind,\n id: x.id,\n clipPath: x.clipPath,\n width: x.width,\n height: x.height,\n }));\n log(\"log\", \"[MultifocalDiagnostics] summary\", {\n ok: results.ok.length,\n failed: results.failed.length,\n streamsDir,\n okStreams: okIds,\n });\n const resultsPath = join(runDir, \"multifocal_diagnostics.json\");\n writeJson(resultsPath, results);\n return { runDir, resultsPath, streamsDir };\n}\n\n/**\n * Parameters for running all diagnostics consecutively.\n */\nexport interface RunAllDiagnosticsConsecutivelyParams {\n /** ReolinkBaichuanApi instance */\n api: ReolinkBaichuanApi;\n /** Base output directory. A timestamped subfolder will be created for each run. */\n outDir: string;\n channel?: number;\n /** Stream sampling duration. */\n durationSeconds: number;\n /** Snapshot interval used for RTSP/RTMP snapshots. Default: 2 seconds. */\n snapshotIntervalSeconds?: number;\n /** Which kinds/profiles to sample. */\n selection: StreamSamplingSelection;\n\n /** Enable CGI diagnostics. `true` uses the same host/creds as the Baichuan client. */\n cgi?: boolean | Partial<ReolinkHttpClientOptions>;\n /** Enable RTSP sampling. `true` uses the same host/creds as the Baichuan client. */\n rtsp?: boolean | Partial<NonNullable<StreamSamplingOptions[\"rtsp\"]>>;\n /** Optional RTMP URLs for each profile. */\n rtmp?: StreamSamplingOptions[\"rtmp\"];\n /** Optional dump limits (native raw frames). */\n limits?: StreamSamplingOptions[\"limits\"];\n\n /** Extra user-provided metadata written into diagnostics.json */\n extra?: Record<string, unknown>;\n /** Logger for progress messages */\n logger?: Logger;\n /** Host for CGI/RTSP (if not provided in cgi/rtsp options) */\n host: string;\n /** Username for CGI/RTSP (if not provided in cgi/rtsp options) */\n username: string;\n /** Password for CGI/RTSP (if not provided in cgi/rtsp options) */\n password: string;\n}\n\n/**\n * Run all diagnostics consecutively: collect diagnostics bundle, sample streams, and create zip archive.\n *\n * @param params - Configuration parameters\n * @returns Results including run directory, zip path, diagnostics path, and streams directory\n */\nexport async function runAllDiagnosticsConsecutively(\n params: RunAllDiagnosticsConsecutivelyParams,\n): Promise<{\n runDir: string;\n zipPath: string;\n diagnosticsPath: string;\n streamsDir: string;\n}> {\n const { api, logger, host, username, password } = params;\n const channel = params.channel ?? 0;\n const log = (msg: string, data?: unknown) => {\n if (logger?.log) {\n if (data !== undefined) logger.log(msg, data);\n else logger.log(msg);\n } else {\n console.log(msg);\n if (data !== undefined) console.log(JSON.stringify(data, null, 2));\n }\n };\n\n const baseOutDir = params.outDir;\n const runDirName = new Date().toISOString().replace(/[:.]/g, \"-\");\n const runDir = join(baseOutDir, runDirName);\n\n log(\"[Diagnostics] starting run\", {\n outDir: baseOutDir,\n runDir,\n channel,\n durationSeconds: params.durationSeconds,\n selection: params.selection,\n });\n\n const cgiEnabled =\n params.cgi === true ||\n (typeof params.cgi === \"object\" && params.cgi != null);\n const cgiApi = cgiEnabled\n ? new ReolinkCgiApiImpl({\n host:\n (typeof params.cgi === \"object\" ? params.cgi.host : undefined) ??\n host,\n username:\n (typeof params.cgi === \"object\" ? params.cgi.username : undefined) ??\n username,\n password:\n (typeof params.cgi === \"object\" ? params.cgi.password : undefined) ??\n password,\n ...(logger ? { logger } : {}),\n ...(api.client.getDebugConfig?.()\n ? { debugConfig: api.client.getDebugConfig?.() }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.port != null\n ? { port: params.cgi.port }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.useHttps != null\n ? { useHttps: params.cgi.useHttps }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.insecureTLS != null\n ? { insecureTLS: params.cgi.insecureTLS }\n : {}),\n ...(typeof params.cgi === \"object\" && params.cgi.timeoutMs != null\n ? { timeoutMs: params.cgi.timeoutMs }\n : {}),\n })\n : undefined;\n\n const diagnosticsRes = await createDiagnosticsBundle({\n outDir: runDir,\n native: { api, channel },\n ...(cgiApi ? { cgi: { cgi: cgiApi, channel } } : {}),\n ...(params.extra ? { extra: params.extra } : {}),\n });\n\n log(\"[Diagnostics] diagnostics bundle collected\", {\n diagnosticsPath: diagnosticsRes.diagnosticsPath,\n runDir: diagnosticsRes.outDir,\n cgiEnabled,\n });\n\n const streamsDir = join(runDir, \"streams\");\n const rtspEnabled =\n params.rtsp === true ||\n (typeof params.rtsp === \"object\" && params.rtsp != null);\n const rtspCfg: StreamSamplingOptions[\"rtsp\"] | undefined = rtspEnabled\n ? {\n host:\n (typeof params.rtsp === \"object\" ? params.rtsp.host : undefined) ??\n host,\n username:\n (typeof params.rtsp === \"object\"\n ? params.rtsp.username\n : undefined) ?? username,\n password:\n (typeof params.rtsp === \"object\"\n ? params.rtsp.password\n : undefined) ?? password,\n ...(typeof params.rtsp === \"object\" && params.rtsp.port != null\n ? { port: params.rtsp.port }\n : {}),\n }\n : undefined;\n\n await sampleStreams({\n outDir: streamsDir,\n durationSeconds: params.durationSeconds,\n ...(params.snapshotIntervalSeconds != null\n ? { snapshotIntervalSeconds: params.snapshotIntervalSeconds }\n : {}),\n channel,\n selection: params.selection,\n ...(rtspCfg ? { rtsp: rtspCfg } : {}),\n ...(params.rtmp ? { rtmp: params.rtmp } : {}),\n native: { api },\n ...(params.limits ? { limits: params.limits } : {}),\n ...(logger ? { logger } : {}),\n });\n\n log(\"[Diagnostics] stream sampling completed\", { streamsDir });\n\n const zipPath = join(baseOutDir, `${runDirName}.zip`);\n\n log(\"[Diagnostics] creating zip bundle\", { zipPath });\n await zipDirectory({ sourceDir: runDir, zipPath });\n log(\"[Diagnostics] zip bundle created\", { zipPath });\n\n return {\n runDir: diagnosticsRes.outDir,\n zipPath,\n diagnosticsPath: diagnosticsRes.diagnosticsPath,\n streamsDir,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Model Fixture Capture — dump all API responses for a device/channel\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Sensitive data sanitization for fixtures\n// ---------------------------------------------------------------------------\n\n/** Keys whose values are always fully masked. */\nconst REDACT_KEYS = new Set([\n \"password\", \"pass\", \"token\", \"secret\", \"apiKey\", \"api_key\",\n]);\n\n/** Keys whose values are partially masked (show type/length hint). */\nconst MASK_KEYS = new Set([\n \"serialNumber\", \"serial\", \"uid\", \"mac\", \"ssid\", \"wifiPassword\",\n \"userName\", \"username\", \"user\",\n]);\n\n/** Regex for IPv4 addresses (private ranges we want to mask). */\nconst IPV4_RE = /\\b(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\b/g;\n\n/** Regex for MAC addresses. */\nconst MAC_RE = /\\b([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}\\b/g;\n\nfunction maskIp(ip: string): string {\n // Keep the first octet, mask the rest: 192.x.x.x → 192.***.***.***\n const parts = ip.split(\".\");\n return `${parts[0]}.***.***.***`;\n}\n\nfunction maskMac(mac: string): string {\n const sep = mac.includes(\"-\") ? \"-\" : \":\";\n const parts = mac.split(/[:-]/);\n return `${parts[0]}${sep}${parts[1]}${sep}**${sep}**${sep}**${sep}**`;\n}\n\nfunction maskSerial(val: string): string {\n if (val.length <= 4) return \"****\";\n return val.slice(0, 2) + \"*\".repeat(val.length - 4) + val.slice(-2);\n}\n\nfunction sanitizeString(s: string): string {\n let out = s;\n // Mask credentials in URLs: ://user:pass@ → ://***:***@\n out = out.replace(/:\\/\\/([^:@]+):([^@]+)@/g, \"://***:***@\");\n // Mask password= query params\n out = out.replace(/(password=)[^&\\s]*/gi, \"$1***\");\n // Mask user= query params\n out = out.replace(/(user=)[^&\\s]*/gi, \"$1***\");\n // Mask IPs\n out = out.replace(IPV4_RE, (match) => maskIp(match));\n // Mask MACs\n out = out.replace(MAC_RE, (match) => maskMac(match));\n return out;\n}\n\n/**\n * Deep-clone and sanitize a value, masking passwords, IPs, MACs, serial numbers, etc.\n */\nexport function sanitizeFixtureData(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n\n if (typeof value === \"string\") {\n return sanitizeString(value);\n }\n\n if (Array.isArray(value)) {\n return value.map(sanitizeFixtureData);\n }\n\n if (typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n const kLower = k.toLowerCase();\n if (REDACT_KEYS.has(kLower)) {\n out[k] = \"***\";\n } else if (MASK_KEYS.has(kLower) || kLower === \"serialnumber\") {\n out[k] = typeof v === \"string\" ? maskSerial(v) : \"***\";\n } else if (kLower === \"mac\") {\n out[k] = typeof v === \"string\" ? maskMac(v) : \"***\";\n } else if (kLower === \"ip\" || kLower === \"ipaddress\" || kLower === \"host\") {\n out[k] = typeof v === \"string\" ? maskIp(v) : v;\n } else if (kLower === \"name\" && typeof v === \"string\" && k !== \"name\") {\n // Don't mask device/camera \"name\" — only mask if it looks like a hostname\n out[k] = v;\n } else {\n out[k] = sanitizeFixtureData(v);\n }\n }\n return out;\n }\n\n return value;\n}\n\n/**\n * Compute the expected stream socket compatibility matrix for a device.\n *\n * This is a pure function (no I/O) that encodes the Baichuan protocol rules:\n * - streamType: main=0, sub=1, ext=0 (default variant)\n * - Two streams with the same streamType on one socket → only one gets frames\n * - Multifocal: camera rejects same profile from different channels (error 430)\n *\n * Use this to validate live test results or to generate expected results for\n * cameras that are not physically available for testing.\n */\nexport function computeExpectedStreamCompatibility(params: {\n /** Number of stream channels (1 for single-lens, 2 for multifocal). */\n channelCount: number;\n /** Profiles available on each channel. */\n profiles?: Array<\"main\" | \"sub\" | \"ext\">;\n}): Array<{\n pair: [string, string];\n expectedOk: boolean;\n reason: string;\n}> {\n const { channelCount } = params;\n const profiles = params.profiles ?? ([\"main\", \"sub\", \"ext\"] as const);\n const expectedStreamType: Record<string, number> = { main: 0, sub: 1, ext: 0 };\n\n type StreamId = { ch: number; profile: string; label: string };\n const allStreams: StreamId[] = [];\n for (let ch = 0; ch < channelCount; ch++) {\n for (const p of profiles) {\n allStreams.push({ ch, profile: p, label: channelCount > 1 ? `ch${ch}_${p}` : p });\n }\n }\n\n const results: Array<{ pair: [string, string]; expectedOk: boolean; reason: string }> = [];\n for (let i = 0; i < allStreams.length; i++) {\n for (let j = i + 1; j < allStreams.length; j++) {\n const a = allStreams[i]!;\n const b = allStreams[j]!;\n const stA = expectedStreamType[a.profile] ?? 0;\n const stB = expectedStreamType[b.profile] ?? 0;\n const sameChannel = a.ch === b.ch;\n\n let expectedOk: boolean;\n let reason: string;\n\n if (sameChannel && stA === stB) {\n expectedOk = false;\n reason = `same streamType (${stA}) on same channel`;\n } else if (!sameChannel && a.profile === b.profile) {\n expectedOk = false;\n reason = `multifocal rejects same profile (${a.profile}) across channels`;\n } else if (!sameChannel && stA === stB) {\n expectedOk = false;\n reason = `same streamType (${stA}) across channels`;\n } else {\n expectedOk = true;\n reason = `different streamTypes (${stA} vs ${stB})`;\n }\n\n results.push({ pair: [a.label, b.label], expectedOk, reason });\n }\n }\n return results;\n}\n\nexport interface ModelFixtureCaptureResult {\n /** Per-call results: command name → ok/error */\n calls: Record<string, { ok: true; value?: unknown } | { ok: false; error: string }>;\n /** Output directory where fixtures were written */\n outDir: string;\n /** Summary counters */\n summary: { total: number; ok: number; failed: number; errors: string[] };\n}\n\n/**\n * Capture all relevant API responses from a single device (or NVR channel)\n * and write them as JSON/XML fixtures into `outDir`.\n *\n * This is the library-level building block used by both:\n * - `test/capture-model-fixtures.ts` (CLI)\n * - the Scrypted plugin's \"Dump Model Fixtures\" setting action\n *\n * For NVR/Hub devices, call once per active channel.\n */\nexport async function captureModelFixtures(params: {\n api: ReolinkBaichuanApi;\n channel: number;\n outDir: string;\n /** Logger (defaults to console) */\n log?: (...args: unknown[]) => void;\n /** Skip the stream combination test (useful for NVR channels where the test should be done separately per-camera) */\n skipStreamCombinationTest?: boolean;\n}): Promise<ModelFixtureCaptureResult> {\n const { api, channel, outDir } = params;\n const log = params.log ?? console.log;\n\n mkdirp(outDir);\n\n // Sanitized writers — mask passwords, IPs, MACs, serial numbers before persisting\n const writeJsonSafe = (filePath: string, data: unknown) =>\n writeJson(filePath, sanitizeFixtureData(data));\n const writeTextSafe = (filePath: string, text: string) =>\n writeText(filePath, sanitizeString(text));\n\n const calls: ModelFixtureCaptureResult[\"calls\"] = {};\n const errors: string[] = [];\n\n async function capture<T>(\n name: string,\n fn: () => Promise<T>,\n writer?: (value: T) => void,\n ): Promise<T | undefined> {\n try {\n const value = await fn();\n calls[name] = { ok: true, value };\n if (writer && value !== undefined && value !== null) {\n writer(value);\n }\n log(` ✓ ${name}`);\n return value;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n calls[name] = { ok: false, error: msg };\n errors.push(`${name}: ${msg}`);\n log(` ✗ ${name}: ${msg}`);\n return undefined;\n }\n }\n\n // ── Device Info ──────────────────────────────────────────────────────────\n const info = await capture(\"getInfo\", () => api.getInfo(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"device-info.json\"), v),\n );\n\n // ── Support Info ─────────────────────────────────────────────────────────\n const support = await capture(\"getSupportInfo\", () => api.getSupportInfo(), (v) =>\n writeJsonSafe(path.join(outDir, \"support-info.json\"), v),\n );\n\n // ── Ability Info ─────────────────────────────────────────────────────────\n const abilities = await capture(\"getAbilityInfo\", () => api.getAbilityInfo(), (v) =>\n writeJsonSafe(path.join(outDir, \"ability-info.json\"), v),\n );\n\n // ── Device Capabilities (full, with probe) ───────────────────────────────\n await capture(\"getDeviceCapabilities\", () => api.getDeviceCapabilities(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"capabilities.json\"), v),\n );\n\n // ── cmd 289 — White LED / Floodlight (raw XML) ──────────────────────────\n await capture(\"cmd289-WhiteLed\", () => api.sendXml({\n cmdId: BC_CMD_ID_GET_WHITE_LED,\n channel,\n timeoutMs: 3000,\n }), (v) => writeTextSafe(path.join(outDir, \"cmd289-white-led.xml\"), v as string));\n\n // ── Stream Metadata (encoding info) ──────────────────────────────────────\n await capture(\"getStreamMetadata\", () => api.getStreamMetadata(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"stream-metadata.json\"), v),\n );\n\n // ── Encoding Config (raw XML) ────────────────────────────────────────────\n await capture(\"getEncXml\", () => api.getEncXml(channel), (v) =>\n writeTextSafe(path.join(outDir, \"enc-config.xml\"), v as string),\n );\n\n // ── Ports ────────────────────────────────────────────────────────────────\n await capture(\"getPorts\", () => api.getPorts(), (v) =>\n writeJsonSafe(path.join(outDir, \"ports.json\"), v),\n );\n\n // ── Talk Ability (intercom) ──────────────────────────────────────────────\n await capture(\"getTalkAbility\", () => api.getTalkAbility(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"talk-ability.json\"), v),\n );\n\n // ── Two-Way Audio Config ─────────────────────────────────────────────────\n await capture(\"getTwoWayAudioConfig\", () => api.getTwoWayAudioConfig(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"two-way-audio-config.json\"), v),\n );\n\n // ── AI State ─────────────────────────────────────────────────────────────\n await capture(\"getAiState\", () => api.getAiState(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"ai-state.json\"), v),\n );\n\n // ── AI Config (autotracking) ─────────────────────────────────────────────\n await capture(\"getAiCfg\", () => api.getAiCfg(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"ai-cfg.json\"), v),\n );\n\n // ── OSD ──────────────────────────────────────────────────────────────────\n await capture(\"getOsd\", () => api.getOsd(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"osd.json\"), v),\n );\n\n // ── Motion Alarm ─────────────────────────────────────────────────────────\n await capture(\"getMotionAlarm\", () => api.getMotionAlarm(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"motion-alarm.json\"), v),\n );\n\n // ── Record Config ────────────────────────────────────────────────────────\n await capture(\"getRecordCfg\", () => api.getRecordCfg(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"record-cfg.json\"), v),\n );\n\n // ── Video Input (image settings) ─────────────────────────────────────────\n await capture(\"getVideoInput\", () => api.getVideoInput(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"video-input.json\"), v),\n );\n\n // ── PTZ Presets ──────────────────────────────────────────────────────────\n await capture(\"getPtzPresets\", () => api.getPtzPresets(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"ptz-presets.json\"), v),\n );\n\n // ── Network Info ─────────────────────────────────────────────────────────\n await capture(\"getNetworkInfo\", () => api.getNetworkInfo(), (v) =>\n writeJsonSafe(path.join(outDir, \"network-info.json\"), v),\n );\n\n // ── System General ───────────────────────────────────────────────────────\n await capture(\"getSystemGeneral\", () => api.getSystemGeneral(), (v) =>\n writeJsonSafe(path.join(outDir, \"system-general.json\"), v),\n );\n\n // ── WiFi Signal ──────────────────────────────────────────────────────────\n await capture(\"getWifiSignal\", () => api.getWifiSignal(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"wifi-signal.json\"), v),\n );\n\n // ── White LED State (parsed) ─────────────────────────────────────────────\n await capture(\"getWhiteLedState\", () => api.getWhiteLedState(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"white-led-state.json\"), v),\n );\n\n // ── Floodlight-on-motion ─────────────────────────────────────────────────\n await capture(\"getFloodlightOnMotion\", () => api.getFloodlightOnMotion(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"floodlight-on-motion.json\"), v),\n );\n\n // ── Video Stream Options ─────────────────────────────────────────────────\n await capture(\"buildVideoStreamOptions\", () => api.buildVideoStreamOptions({ channel }), (v) =>\n writeJsonSafe(path.join(outDir, \"video-stream-options.json\"), v),\n );\n\n // ── Multifocal / Dual-Lens Info ────────────────────────────────────────\n await capture(\"getDualLensChannelInfo\", () => api.getDualLensChannelInfo(channel), (v) =>\n writeJsonSafe(path.join(outDir, \"dual-lens-info.json\"), v),\n );\n\n // ── Stream Combination Test ────────────────────────────────────────────\n // Tests which stream pairs can share a single TCP socket without\n // streamType mismatches.\n //\n // Single-lens: tests all pairs on the same channel (main+sub, main+ext, sub+ext).\n // Multifocal: also tests cross-channel pairs (ch0_main+ch1_sub, etc.) since\n // multifocal cameras have strict constraints on which stream\n // combinations are allowed per socket (see resolveSocketTag).\n //\n // Skipped when skipStreamCombinationTest is true (e.g. NVR channels where\n // the caller manages per-camera stream testing separately).\n if (!params.skipStreamCombinationTest) await capture(\"streamCombinationTest\", async () => {\n // Detect multifocal\n let dualLensInfo: any;\n try { dualLensInfo = await api.getDualLensChannelInfo(channel); } catch { /* ignore */ }\n const isMultifocal = dualLensInfo?.isDualLens === true;\n const channelCount: number = dualLensInfo?.streamChannelCount ?? 1;\n\n // Build list of stream identifiers: { channel, profile, label }\n type StreamId = { ch: number; profile: StreamProfile; label: string };\n const allStreams: StreamId[] = [];\n const profiles: StreamProfile[] = [\"main\", \"sub\", \"ext\"];\n for (let ch = 0; ch < channelCount; ch++) {\n for (const p of profiles) {\n const label = channelCount > 1 ? `ch${ch}_${p}` : p;\n allStreams.push({ ch, profile: p, label });\n }\n }\n\n // Generate all unique pairs\n const pairs: Array<[StreamId, StreamId]> = [];\n for (let i = 0; i < allStreams.length; i++) {\n for (let j = i + 1; j < allStreams.length; j++) {\n pairs.push([allStreams[i]!, allStreams[j]!]);\n }\n }\n\n // Expected compatibility based on protocol rules:\n // - streamType: main=0, sub=1, ext=0 (default variant)\n // - Two streams with the same streamType on the same socket → only one produces frames\n // - Multifocal: camera rejects two \"main\" or two \"sub\" from different channels (error 430)\n const expectedStreamType: Record<StreamProfile, number> = { main: 0, sub: 1, ext: 0 };\n\n const expectedCompat: Array<{ pair: [string, string]; expectedOk: boolean; reason: string }> = [];\n for (const [a, b] of pairs) {\n const stA = expectedStreamType[a.profile]!;\n const stB = expectedStreamType[b.profile]!;\n const sameChannel = a.ch === b.ch;\n\n let expectedOk: boolean;\n let reason: string;\n\n if (sameChannel && stA === stB) {\n // Same channel, same streamType → conflict (ext=0 clashes with main=0)\n expectedOk = false;\n reason = `same streamType (${stA}) on same channel`;\n } else if (!sameChannel && a.profile === b.profile) {\n // Cross-channel, same profile → multifocal rejects this (error 430)\n expectedOk = false;\n reason = `multifocal rejects same profile (${a.profile}) across channels`;\n } else if (!sameChannel && stA === stB) {\n // Cross-channel, different profiles but same streamType\n // e.g., ch0_main (st=0) + ch1_ext (st=0) → conflict\n expectedOk = false;\n reason = `same streamType (${stA}) across channels`;\n } else {\n expectedOk = true;\n reason = `different streamTypes (${stA} vs ${stB})`;\n }\n\n expectedCompat.push({ pair: [a.label, b.label], expectedOk, reason });\n }\n\n // Run live tests\n interface ComboResult {\n pair: [string, string];\n ok: boolean;\n framesA: number;\n framesB: number;\n mismatches: number;\n error?: string;\n durationMs: number;\n expectedOk: boolean;\n expectedReason: string;\n matchesExpected: boolean;\n }\n const results: ComboResult[] = [];\n const TEST_DURATION_MS = 4000;\n\n for (let pairIdx = 0; pairIdx < pairs.length; pairIdx++) {\n const [a, b] = pairs[pairIdx]!;\n const expected = expectedCompat[pairIdx]!;\n log(` testing ${a.label}+${b.label} on shared socket...`);\n\n let session: { client: any; release: () => Promise<void> } | undefined;\n try {\n session = await api.createDedicatedSession(\n `test:combo:${a.label}_${b.label}`,\n );\n } catch (e) {\n const result: ComboResult = {\n pair: [a.label, b.label],\n ok: false, framesA: 0, framesB: 0, mismatches: 0,\n error: `session: ${e instanceof Error ? e.message : String(e)}`,\n durationMs: 0,\n expectedOk: expected.expectedOk,\n expectedReason: expected.reason,\n matchesExpected: !expected.expectedOk, // failed as expected if we expected failure\n };\n results.push(result);\n continue;\n }\n\n const testClient = session.client;\n let framesA = 0;\n let framesB = 0;\n let mismatches = 0;\n const start = Date.now();\n\n const stTypes: Record<StreamProfile, Set<number>> = {\n main: new Set([0, 2]), sub: new Set([1, 3]), ext: new Set([0, 2]),\n };\n const setA = stTypes[a.profile]!;\n const setB = stTypes[b.profile]!;\n\n const onFrame = (frame: any) => {\n if (frame.header?.cmdId !== BC_CMD_ID_VIDEO) return;\n const st = frame.header.streamType;\n if (setA.has(st)) framesA++;\n else if (setB.has(st)) framesB++;\n else mismatches++;\n };\n testClient.on(\"frame\", onFrame);\n\n let error: string | undefined;\n try {\n await api.startVideoStream(a.ch, a.profile, { client: testClient });\n await api.startVideoStream(b.ch, b.profile, { client: testClient });\n await new Promise((r) => setTimeout(r, TEST_DURATION_MS));\n await api.stopVideoStream(a.ch, a.profile, { client: testClient }).catch(() => {});\n await api.stopVideoStream(b.ch, b.profile, { client: testClient }).catch(() => {});\n } catch (e) {\n error = e instanceof Error ? e.message : String(e);\n } finally {\n testClient.removeListener(\"frame\", onFrame);\n await session.release().catch(() => {});\n }\n\n const elapsed = Date.now() - start;\n const ok = !error && framesA > 0 && framesB > 0 && mismatches === 0;\n\n const result: ComboResult = {\n pair: [a.label, b.label], ok, framesA, framesB, mismatches,\n ...(error ? { error } : {}),\n durationMs: elapsed,\n expectedOk: expected.expectedOk,\n expectedReason: expected.reason,\n matchesExpected: ok === expected.expectedOk,\n };\n results.push(result);\n\n const matchStr = result.matchesExpected ? \"\" : \" *** UNEXPECTED ***\";\n log(` ${a.label}+${b.label}: ${ok ? \"OK\" : \"FAIL\"} (expected=${expected.expectedOk ? \"OK\" : \"FAIL\"}) A=${framesA} B=${framesB} mismatch=${mismatches}${matchStr}`);\n }\n\n const unexpected = results.filter((r) => !r.matchesExpected);\n return {\n isMultifocal,\n channelCount,\n results,\n summary: {\n total: results.length,\n ok: results.filter((r) => r.ok).length,\n failed: results.filter((r) => !r.ok).length,\n matchesExpected: results.filter((r) => r.matchesExpected).length,\n unexpected: unexpected.map((r) => `${r.pair[0]}+${r.pair[1]}: got ${r.ok ? \"OK\" : \"FAIL\"}, expected ${r.expectedOk ? \"OK\" : \"FAIL\"}`),\n },\n };\n }, (v) => writeJsonSafe(path.join(outDir, \"stream-combination-test.json\"), v));\n\n // ── Summary ──────────────────────────────────────────────────────────────\n const total = Object.keys(calls).length;\n const ok = Object.values(calls).filter((c) => c.ok).length;\n const failed = total - ok;\n\n const summary = { total, ok, failed, errors };\n writeJsonSafe(path.join(outDir, \"_summary.json\"), {\n collectedAt: new Date().toISOString(),\n model: info?.type ?? \"unknown\",\n itemNo: (info as any)?.itemNo ?? \"unknown\",\n firmwareVersion: info?.firmwareVersion ?? \"unknown\",\n channel,\n ...summary,\n calls: Object.fromEntries(\n Object.entries(calls).map(([k, v]) => [\n k,\n v.ok ? \"ok\" : `FAILED: ${(v as any).error}`,\n ]),\n ),\n });\n\n log(`\\n Summary: ${ok}/${total} ok, ${failed} failed`);\n if (errors.length) {\n log(` Errors:`);\n for (const err of errors) {\n log(` - ${err}`);\n }\n }\n\n return { calls, outDir, summary };\n}\n","import type {\n ParsedRecordingFileName,\n RecordingDevType,\n RecordingVodFlags,\n RecordingVodStreamHint,\n} from \"./types\";\n\ntype FlagSpec = Record<string, readonly [number, number]>;\n\nconst FLAGS_CAM_V2: FlagSpec = {\n resolution_index: [0, 7],\n tv_system: [7, 1],\n framerate: [8, 7],\n audio_index: [15, 2],\n ai_pd: [17, 1],\n ai_fd: [18, 1],\n ai_vd: [19, 1],\n ai_ad: [20, 1],\n encoder_type_index: [21, 2],\n is_schedule_record: [23, 1],\n is_motion_record: [24, 1],\n is_rf_record: [25, 1],\n is_doorbell_record: [26, 1],\n ai_other: [27, 1],\n};\n\nconst FLAGS_HUB_V0: FlagSpec = {\n resolution_index: [0, 7],\n tv_system: [7, 1],\n framerate: [8, 7],\n audio_index: [15, 2],\n ai_pd: [17, 1],\n ai_fd: [18, 1],\n ai_vd: [19, 1],\n ai_ad: [20, 1],\n encoder_type_index: [21, 2],\n is_schedule_record: [23, 1],\n is_motion_record: [24, 1],\n is_rf_record: [25, 1],\n is_doorbell_record: [26, 1],\n is_ai_other_record: [27, 1],\n picture_layout_index: [28, 7],\n package_delivered: [35, 1],\n package_takenaway: [36, 1],\n};\n\nconst FLAGS_HUB_V1: FlagSpec = {\n ...FLAGS_HUB_V0,\n package_event: [37, 1],\n};\n\nconst FLAGS_HUB_V2: FlagSpec = {\n resolution_index: [0, 7],\n tv_system: [7, 1],\n framerate: [8, 7],\n audio_index: [15, 2],\n ai_pd: [17, 1],\n ai_fd: [18, 1],\n ai_vd: [19, 1],\n ai_ad: [20, 1],\n ai_other: [21, 2],\n encoder_type_index: [23, 1],\n is_schedule_record: [24, 1],\n is_motion_record: [25, 1],\n is_rf_record: [26, 1],\n is_doorbell_record: [27, 1],\n picture_layout_index: [28, 7],\n package_delivered: [35, 1],\n package_takenaway: [36, 1],\n package_event: [37, 1],\n upload_flag: [38, 1],\n};\n\nconst FLAGS_MAPPING: Record<RecordingDevType, Record<number, FlagSpec>> = {\n cam: {\n 2: FLAGS_CAM_V2,\n 3: FLAGS_CAM_V2,\n 4: FLAGS_CAM_V2,\n 7: FLAGS_CAM_V2,\n 9: FLAGS_CAM_V2,\n 10: FLAGS_CAM_V2,\n },\n hub: {\n 0: FLAGS_HUB_V0,\n 1: FLAGS_HUB_V1,\n 2: FLAGS_HUB_V2,\n // NOTE: Home Hub / NVR firmware may emit hub filenames with higher versions (e.g. RecM04)\n // and longer hex payloads. We fall back to the highest known hub mapping (v2)\n // by not hardcoding a guessed v4 mapping.\n },\n};\n\nfunction decodeHexToFlags(\n hexValue: string,\n mapping: FlagSpec,\n): Record<string, number> | undefined {\n if (!hexValue) return undefined;\n if (!/^[0-9a-fA-F]+$/.test(hexValue)) return undefined;\n\n const bitLen = hexValue.length * 4;\n const hexInt = BigInt(`0x${hexValue}`);\n const bin = hexInt.toString(2).padStart(bitLen, \"0\");\n const revBin = bin.split(\"\").reverse().join(\"\");\n const revInt = BigInt(`0b${revBin}`);\n\n const out: Record<string, number> = {};\n for (const [flag, [bitPosition, bitSize]] of Object.entries(mapping)) {\n const pos = BigInt(bitPosition);\n const size = BigInt(bitSize);\n const mask = ((1n << size) - 1n) << pos;\n const segRev = (revInt & mask) >> pos;\n const segBin = segRev\n .toString(2)\n .padStart(bitSize, \"0\")\n .split(\"\")\n .reverse()\n .join(\"\");\n out[flag] = Number.parseInt(segBin, 2);\n }\n return out;\n}\n\nfunction decodeKnownFlags(\n devType: RecordingDevType,\n version: number,\n hexValue: string,\n): { raw: Record<string, number>; flags: RecordingVodFlags } | undefined {\n const versions = FLAGS_MAPPING[devType];\n const mapping =\n versions[version] ??\n versions[Math.max(...Object.keys(versions).map((k) => Number(k)))];\n if (!mapping) return undefined;\n const raw = decodeHexToFlags(hexValue, mapping);\n if (!raw) return undefined;\n\n const flags: RecordingVodFlags = {\n aiPerson: raw.ai_pd === 1,\n aiVehicle: raw.ai_vd === 1,\n aiAnimal: raw.ai_ad === 1,\n aiFace: raw.ai_fd === 1,\n aiOther:\n raw.is_ai_other_record === 1 ||\n raw.ai_other === 1 ||\n (raw.ai_other ?? 0) > 0,\n schedule: raw.is_schedule_record === 1,\n motion: raw.is_motion_record === 1,\n rf: raw.is_rf_record === 1,\n doorbell: raw.is_doorbell_record === 1,\n package:\n raw.package_event === 1 ||\n raw.package_delivered === 1 ||\n raw.package_takenaway === 1,\n };\n\n return { raw, flags };\n}\n\nfunction parseDateTimeLocal(\n yyyymmdd: string,\n hhmmss: string,\n): Date | undefined {\n if (!/^\\d{8}$/.test(yyyymmdd)) return undefined;\n if (!/^\\d{6}$/.test(hhmmss)) return undefined;\n const year = Number.parseInt(yyyymmdd.slice(0, 4), 10);\n const month = Number.parseInt(yyyymmdd.slice(4, 6), 10);\n const day = Number.parseInt(yyyymmdd.slice(6, 8), 10);\n const hour = Number.parseInt(hhmmss.slice(0, 2), 10);\n const minute = Number.parseInt(hhmmss.slice(2, 4), 10);\n const second = Number.parseInt(hhmmss.slice(4, 6), 10);\n if (![year, month, day, hour, minute, second].every(Number.isFinite))\n return undefined;\n // IMPORTANT: Parse as LOCAL TIME because the camera stores timestamps in local time in the filename.\n // When we create a Date object with new Date(year, month, day, hour, minute, second), JavaScript\n // interprets these values as local time and stores the date internally as UTC timestamp.\n // This means getTime() will return the correct UTC timestamp that represents that local time moment.\n // This is correct because the filename values represent the actual local time shown on the camera.\n return new Date(year, month - 1, day, hour, minute, second);\n}\n\n/**\n * Best-effort parse of Reolink VOD recording filenames.\n *\n * Formats observed:\n * - Mp4Record/2020-12-22/RecM01_20201222_075939_080140_<HEX>_<SIZE>.mp4\n * - Mp4Record/2023-04-26/RecS02_DST20230426_145918_150032_<HEX>_<SIZE>.mp4\n * - .../RecS07_20250219_111146_111238_0_<HEX>_<SIZE>.mp4\n * - .../RecM02_DST20240827_090302_090334_0_800_800_<HEX>_<SIZE>.mp4\n * - 0120260107000000 (numeric identifier format: [channel][YYYYMMDD][HHMMSS])\n * - 829_0_900_510_033C8200000000_184C8B5.mp4 (NVR compact format)\n */\nexport function parseRecordingFileName(\n fileName: string,\n): ParsedRecordingFileName | undefined {\n // Try numeric identifier format first (e.g., \"0120260107000000\")\n // Format: [channel][YYYYMMDD][HHMMSS] where channel is 2 digits, date is 8 digits, time is 6 digits\n const numericMatch = /^(\\d{2})(\\d{8})(\\d{6})$/.exec(fileName);\n if (numericMatch) {\n const channelStr = numericMatch[1];\n const dateStr = numericMatch[2];\n const timeStr = numericMatch[3];\n if (channelStr && dateStr && timeStr) {\n const start = parseDateTimeLocal(dateStr, timeStr);\n if (start) {\n // For numeric format, assume 20 second duration (common for motion detection clips)\n const end = new Date(start.getTime() + 20_000);\n return {\n baseName: fileName,\n ext: \"\",\n streamHint: \"main\",\n version: 0,\n devType: \"cam\",\n start,\n end,\n durationMs: 20_000,\n };\n }\n }\n }\n\n // Try NVR compact format: {timestamp}_{channel}_{duration?}_{flags1}_{hexFlags}_{hash}.mp4\n // Example: 829_0_900_510_033C8200000000_184C8B5.mp4\n // The hexFlags field contains video metadata including framerate\n const nvrCompactMatch =\n /^(\\d+)_(\\d+)_(\\d+)_(\\d+)_([0-9A-Fa-f]+)_[0-9A-Fa-f]+\\.mp4$/i.exec(\n fileName,\n );\n if (nvrCompactMatch) {\n const [, , channelStr, durationField, , hexFlags] = nvrCompactMatch;\n if (hexFlags && hexFlags.length >= 8) {\n // Duration field appears to be in deciseconds (900 = 90 seconds)\n const durationDs = Number.parseInt(durationField ?? \"0\", 10);\n const durationMs = durationDs > 0 ? durationDs * 100 : 60_000; // Default 60s if unknown\n\n // Try to decode flags using hub format (NVR uses similar structure)\n const decoded = decodeKnownFlags(\"hub\", 2, hexFlags);\n const parsed: ParsedRecordingFileName = {\n baseName: fileName.replace(/\\.mp4$/i, \"\"),\n ext: \"mp4\",\n streamHint: \"main\",\n version: 2,\n devType: \"hub\",\n start: new Date(), // Unknown start time in compact format\n end: new Date(Date.now() + durationMs),\n durationMs,\n };\n\n if (decoded) {\n parsed.flags = decoded.flags;\n parsed.rawFlags = decoded.raw;\n if (decoded.raw.framerate != null && decoded.raw.framerate > 0) {\n parsed.framerate = decoded.raw.framerate;\n }\n }\n\n return parsed;\n }\n }\n\n const dot = fileName.lastIndexOf(\".\");\n if (dot <= 0 || dot === fileName.length - 1) return undefined;\n const ext = fileName.slice(dot + 1);\n const pathNoExt = fileName.slice(0, dot);\n const baseName = pathNoExt.split(\"/\").filter(Boolean).at(-1);\n if (!baseName) return undefined;\n\n const parts = baseName.split(\"_\");\n const prefix = parts[0] ?? \"\";\n if (!prefix.startsWith(\"Rec\") || prefix.length !== 6) return undefined;\n\n const streamChar = prefix[3];\n const streamHint: RecordingVodStreamHint =\n streamChar === \"M\" ? \"main\" : streamChar === \"S\" ? \"sub\" : \"unknown\";\n\n const version = Number.parseInt(prefix.slice(4, 6), 16);\n if (!Number.isFinite(version)) return undefined;\n\n let devType: RecordingDevType = \"cam\";\n let startDate = \"\";\n let startTime = \"\";\n let endTime = \"\";\n let animalTypeRaw: string | undefined;\n let widthRaw: string | undefined;\n let heightRaw: string | undefined;\n let hexValue = \"\";\n let sizeHex: string | undefined;\n\n if (parts.length === 6) {\n // RecM01_20201222_075939_080140_<HEX>_<SIZE>\n startDate = parts[1] ?? \"\";\n startTime = parts[2] ?? \"\";\n endTime = parts[3] ?? \"\";\n hexValue = parts[4] ?? \"\";\n sizeHex = parts[5];\n } else if (parts.length === 7) {\n // RecS07_20250219_111146_111238_0_<HEX>_<SIZE>\n startDate = parts[1] ?? \"\";\n startTime = parts[2] ?? \"\";\n endTime = parts[3] ?? \"\";\n animalTypeRaw = parts[4];\n hexValue = parts[5] ?? \"\";\n sizeHex = parts[6];\n } else if (parts.length === 9) {\n // RecM02_DST20240827_090302_090334_0_800_800_<HEX>_<SIZE>\n devType = \"hub\";\n startDate = parts[1] ?? \"\";\n startTime = parts[2] ?? \"\";\n endTime = parts[3] ?? \"\";\n animalTypeRaw = parts[4];\n widthRaw = parts[5];\n heightRaw = parts[6];\n hexValue = parts[7] ?? \"\";\n sizeHex = parts[8];\n } else {\n return undefined;\n }\n\n startDate = startDate.toLowerCase().replace(\"dst\", \"\");\n const start = parseDateTimeLocal(startDate, startTime);\n if (!start) return undefined;\n const end =\n endTime === \"000000\" ? start : parseDateTimeLocal(startDate, endTime);\n if (!end) return undefined;\n\n const durationMs = Math.max(0, end.getTime() - start.getTime());\n\n const parsed: ParsedRecordingFileName = {\n baseName,\n ext,\n streamHint,\n version,\n devType,\n start,\n end,\n durationMs,\n };\n\n const decoded = decodeKnownFlags(devType, version, hexValue);\n if (decoded) {\n parsed.flags = decoded.flags;\n parsed.rawFlags = decoded.raw;\n // Extract framerate if available in rawFlags\n if (decoded.raw.framerate != null && decoded.raw.framerate > 0) {\n parsed.framerate = decoded.raw.framerate;\n }\n }\n\n if (animalTypeRaw != null) parsed.animalTypeRaw = animalTypeRaw;\n if (widthRaw != null) parsed.widthRaw = widthRaw;\n if (heightRaw != null) parsed.heightRaw = heightRaw;\n\n // Parse file size from hex (last field before extension)\n if (sizeHex && /^[0-9a-fA-F]+$/.test(sizeHex)) {\n const sizeBytes = parseInt(sizeHex, 16);\n if (Number.isFinite(sizeBytes) && sizeBytes > 0) {\n parsed.sizeBytes = sizeBytes;\n }\n }\n\n return parsed;\n}\n","import { Agent } from \"undici\";\nimport {\n ReolinkCmdRequest,\n type LoginResponseValue,\n type ReolinkCmdResponse,\n} from \"./types\";\n\nexport type ReolinkHttpClientOptions = {\n host: string;\n username: string;\n password: string;\n port?: number;\n useHttps?: boolean;\n /** Disable TLS verification for HTTPS (default: true). */\n insecureTLS?: boolean;\n timeoutMs?: number;\n};\n\nexport class ReolinkHttpClient {\n private static readonly sessionPool = new Map<\n string,\n {\n token?: string;\n tokenExpiresAt?: number;\n loginInFlight?: Promise<void>;\n }\n >();\n\n private readonly host: string;\n private readonly username: string;\n private readonly password: string;\n private port: number | undefined;\n private useHttps: boolean | undefined;\n private readonly insecureTLS: boolean;\n private readonly timeoutMs: number;\n\n private token: string | undefined;\n private tokenExpiresAt: number | undefined; // epoch ms\n\n // `fetch` in Node uses `undici` and accepts `dispatcher`, but typings may differ\n // between `undici` and `undici-types` (Node). We keep a permissive type.\n private httpsAgent: unknown;\n\n constructor(opts: ReolinkHttpClientOptions) {\n this.host = opts.host;\n this.username = opts.username;\n this.password = opts.password;\n this.port = opts.port;\n this.useHttps = opts.useHttps;\n this.insecureTLS = opts.insecureTLS ?? true;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n\n if (this.insecureTLS) {\n this.httpsAgent = new Agent({ connect: { rejectUnauthorized: false } });\n }\n }\n\n getUsername(): string {\n return this.username;\n }\n\n getToken(): string | undefined {\n return this.token;\n }\n\n clearToken(): void {\n this.token = undefined;\n this.tokenExpiresAt = undefined;\n }\n\n private sessionKey(): string {\n const scheme = this.useHttps ? \"https\" : \"http\";\n const port = this.port ?? (this.useHttps ? 443 : 80);\n // Token is bound to host + user session. We intentionally do NOT include password here;\n // a password change should be handled by consumer recreating the client or by logout.\n return `${scheme}://${this.host}:${port}|${this.username}`;\n }\n\n private getSharedSession(): {\n token?: string;\n tokenExpiresAt?: number;\n loginInFlight?: Promise<void>;\n } {\n const key = this.sessionKey();\n const existing = ReolinkHttpClient.sessionPool.get(key);\n if (existing) return existing;\n const created: {\n token?: string;\n tokenExpiresAt?: number;\n loginInFlight?: Promise<void>;\n } = {};\n ReolinkHttpClient.sessionPool.set(key, created);\n return created;\n }\n\n private baseUrl(): string {\n const scheme = this.useHttps ? \"https\" : \"http\";\n const port = this.port ?? (this.useHttps ? 443 : 80);\n return `${scheme}://${this.host}:${port}`;\n }\n\n private apiUrl(params: Record<string, string | number | undefined>): string {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v == null) continue;\n qs.set(k, String(v));\n }\n return `${this.baseUrl()}/cgi-bin/api.cgi?${qs.toString()}`;\n }\n\n private isTokenValid(): boolean {\n if (!this.token || !this.tokenExpiresAt) return false;\n // margine 10s\n return Date.now() + 10_000 < this.tokenExpiresAt;\n }\n\n private isSharedTokenValid(shared: {\n token?: string;\n tokenExpiresAt?: number;\n }): boolean {\n if (!shared.token || !shared.tokenExpiresAt) return false;\n return Date.now() + 10_000 < shared.tokenExpiresAt;\n }\n\n private async performLogin(): Promise<void> {\n const body: ReolinkCmdRequest[] = [\n {\n cmd: \"Login\",\n action: 0,\n param: {\n User: {\n userName: this.username,\n password: this.password,\n },\n },\n },\n ];\n\n const rsp = await this.sendJson<LoginResponseValue>(\n body,\n { cmd: \"Login\" },\n { includeToken: false },\n );\n const first = rsp[0];\n if (!first || first.code !== 0 || !first.value?.Token?.name) {\n throw new Error(`Login failed: ${JSON.stringify(rsp)}`);\n }\n\n const lease = Number(first.value.Token.leaseTime);\n const token = first.value.Token.name;\n const expiresAt = Date.now() + (Number.isFinite(lease) ? lease * 1000 : 0);\n\n this.token = token;\n this.tokenExpiresAt = expiresAt;\n\n const shared = this.getSharedSession();\n shared.token = token;\n shared.tokenExpiresAt = expiresAt;\n }\n\n async login(): Promise<void> {\n if (this.isTokenValid()) return;\n\n const shared = this.getSharedSession();\n if (this.isSharedTokenValid(shared)) {\n this.token = shared.token;\n this.tokenExpiresAt = shared.tokenExpiresAt;\n return;\n }\n\n if (shared.loginInFlight) {\n await shared.loginInFlight;\n if (this.isSharedTokenValid(shared)) {\n this.token = shared.token;\n this.tokenExpiresAt = shared.tokenExpiresAt;\n }\n return;\n }\n\n shared.loginInFlight = (async () => {\n try {\n await this.performLogin();\n } finally {\n delete shared.loginInFlight;\n }\n })();\n\n await shared.loginInFlight;\n }\n\n async logout(): Promise<void> {\n if (!this.token) return;\n try {\n await this.call(\"Logout\", { action: 0, param: {} });\n } finally {\n this.clearToken();\n\n const shared = this.getSharedSession();\n delete shared.token;\n delete shared.tokenExpiresAt;\n }\n }\n\n async call<TValue = unknown, TParam = unknown>(\n cmd: string,\n opts?: {\n action?: number;\n param?: TParam; /** Some commands are host-level and do not require a param */\n },\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n await this.login();\n const body: ReolinkCmdRequest<TParam>[] = [\n {\n cmd,\n action: opts?.action ?? 0,\n ...(opts && \"param\" in opts && opts.param !== undefined\n ? { param: opts.param }\n : {}),\n },\n ];\n return await this.sendJson<TValue>(\n body,\n { cmd, token: this.token },\n { includeToken: true },\n );\n }\n\n async callMany<TValue = unknown>(\n cmds: ReolinkCmdRequest[],\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n await this.login();\n // Extract cmd from first request for query string\n const cmd = cmds[0]?.cmd;\n const query: Record<string, string | number | undefined> = {\n token: this.token,\n };\n if (cmd) query.cmd = cmd;\n return await this.sendJson<TValue>(cmds, query, { includeToken: true });\n }\n\n private async sendJson<TValue>(\n body: ReolinkCmdRequest[],\n query: Record<string, string | number | undefined>,\n _opts: { includeToken: boolean },\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n const url = this.apiUrl(query);\n const ac = new AbortController();\n const t = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const init: any = {\n method: \"POST\",\n body: JSON.stringify(body),\n headers: { \"Content-Type\": \"application/json\" },\n signal: ac.signal,\n };\n if (this.useHttps && this.httpsAgent)\n init.dispatcher = this.httpsAgent as any;\n\n const res = await fetch(url, init as RequestInit);\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n const json = JSON.parse(text) as ReolinkCmdResponse<TValue>[];\n return json;\n } finally {\n clearTimeout(t);\n }\n }\n\n /**\n * Take a snapshot via CGI `cmd=Snap` and return the raw JPEG bytes.\n *\n * This is a binary endpoint (not JSON). Requires a valid token.\n */\n async snap(\n channel: number,\n opts?: {\n timeoutMs?: number;\n rs?: string;\n /** Optional snapshot quality selector used by some firmwares (\"main\" | \"sub\"). */\n snapType?: \"main\" | \"sub\";\n /** Optional logic channel for dual-lens models behind NVR/Hub (e.g. TrackMix tele lens). */\n iLogicChannel?: number;\n },\n ): Promise<Buffer> {\n await this.login();\n if (!this.token) throw new Error(\"Missing token after login\");\n\n const rs = opts?.rs ?? Date.now().toString();\n const url = this.apiUrl({\n cmd: \"Snap\",\n channel,\n rs,\n snapType: opts?.snapType,\n iLogicChannel: opts?.iLogicChannel,\n token: this.token,\n });\n\n const ac = new AbortController();\n const t = setTimeout(() => ac.abort(), opts?.timeoutMs ?? this.timeoutMs);\n try {\n const init: any = {\n method: \"GET\",\n signal: ac.signal,\n };\n if (this.useHttps && this.httpsAgent)\n init.dispatcher = this.httpsAgent as any;\n\n const res = await fetch(url, init as RequestInit);\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n const ab = await res.arrayBuffer();\n return Buffer.from(ab);\n } finally {\n clearTimeout(t);\n }\n }\n\n /**\n * Download a VOD/recording via CGI `cmd=Download`.\n *\n * Uses POST with a dummy JSON body and query params.\n * Returns the raw binary payload (often mp4).\n */\n async downloadVod(\n source: string,\n output?: string,\n start?: string,\n ): Promise<Buffer> {\n await this.login();\n if (!this.token) throw new Error(\"Missing token after login\");\n\n // NOTE: Do NOT URL-encode '/' in `source`. Many Reolink firmwares expect raw paths\n // like \"/mnt/sda/...\" and return 404 when `%2F` is used. Only encode spaces.\n const scheme = this.useHttps ? \"https\" : \"http\";\n const port = this.port ?? (this.useHttps ? 443 : 80);\n const sourceForQuery = source.replaceAll(\" \", \"%20\");\n const outputForQuery = output ? encodeURIComponent(output) : undefined;\n const startForQuery = start ? encodeURIComponent(start) : undefined;\n const tokenForQuery = encodeURIComponent(this.token);\n const url = `${scheme}://${this.host}:${port}/cgi-bin/api.cgi?cmd=Download&source=${sourceForQuery}${outputForQuery ? `&output=${outputForQuery}` : \"\"}${startForQuery ? `&start=${startForQuery}` : \"\"}&token=${tokenForQuery}`;\n const ac = new AbortController();\n const t = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const init: any = {\n method: \"POST\",\n body: \"[{}]\",\n headers: { \"Content-Type\": \"application/json\" },\n signal: ac.signal,\n };\n if (this.useHttps && this.httpsAgent)\n init.dispatcher = this.httpsAgent as any;\n\n const res = await fetch(url, init as RequestInit);\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n const ab = await res.arrayBuffer();\n return Buffer.from(ab);\n } finally {\n clearTimeout(t);\n }\n }\n}\n","import type { DebugConfig, Logger } from \"../../debug/DebugConfig\";\nimport { recordingsTraceLog } from \"../../debug/DebugConfig\";\nimport { collectNvrDiagnostics } from \"../../debug/DiagnosticsTools\";\nimport { parseRecordingFileName } from \"../baichuan/recordingFileName\";\nimport type { RecordingDetectionClass, RecordingFile } from \"../baichuan/types\";\nimport {\n ReolinkHttpClient,\n type ReolinkHttpClientOptions,\n} from \"../http/ReolinkHttpClient\";\nimport type { ReolinkCmdRequest, ReolinkCmdResponse } from \"../http/types\";\nimport type { ReolinkDeviceInfo, ReolinkDeviceInfoTag } from \"../types\";\n\nexport type JsonPrimitive = string | number | boolean | null;\nexport type JsonObject = { [key: string]: JsonValue };\nexport type JsonValue = JsonPrimitive | JsonObject | JsonValue[];\n\nexport type ReolinkCmdResponseExt<TValue = JsonValue> =\n ReolinkCmdResponse<TValue> & {\n /** Some CGI commands (notably GetEnc) return additional metadata fields. */\n initial?: JsonValue;\n range?: JsonValue;\n };\n\nexport type CgiChannelStatusEntry = {\n channel: number;\n name?: string;\n online?: number;\n sleep?: number;\n uid?: string;\n typeInfo?: string;\n};\n\nexport type CgiGetChannelstatusValue = {\n status?: CgiChannelStatusEntry[];\n};\n\nexport type CgiChnTypeInfoValue = {\n boardInfo?: string;\n firmVer?: string;\n pakSuffix?: string;\n typeInfo?: string;\n};\n\nexport type CgiDetectionState = {\n alarm_state: number;\n support: number;\n};\n\nexport type CgiAiKey =\n | \"dog_cat\"\n | \"face\"\n | \"other\"\n | \"package\"\n | \"people\"\n | \"vehicle\";\n\nexport type CgiAiStateValue = Partial<Record<CgiAiKey, CgiDetectionState>> & {\n channel: number;\n};\n\nexport type CgiEncStream = {\n bitRate: number;\n frameRate: number;\n gop: number;\n height: number;\n profile: string;\n size: string;\n vType: string;\n width: number;\n};\n\nexport type CgiEnc = {\n audio: number;\n channel: number;\n mainStream: CgiEncStream;\n subStream: CgiEncStream;\n};\n\nexport type CgiEncValue = {\n Enc: CgiEnc;\n};\n\nexport type CgiGetChannelstatusResponse =\n ReolinkCmdResponseExt<CgiGetChannelstatusValue> & {\n cmd: \"GetChannelstatus\";\n };\n\nexport type CgiGetChnTypeInfoResponse =\n ReolinkCmdResponseExt<CgiChnTypeInfoValue> & {\n cmd: \"GetChnTypeInfo\";\n };\n\nexport type CgiGetAiStateResponse = ReolinkCmdResponseExt<CgiAiStateValue> & {\n cmd: \"GetAiState\";\n};\n\nexport type CgiGetEncResponse = ReolinkCmdResponseExt<CgiEncValue> & {\n cmd: \"GetEnc\";\n initial?: CgiEncValue;\n range?: JsonValue;\n};\n\nexport type CgiGetRtspUrlValue = {\n rtspUrl?: string;\n url?: string;\n RtspUrl?: string;\n rtsp?: string;\n} & Record<string, JsonValue>;\n\nexport type CgiGetRtspUrlResponse =\n ReolinkCmdResponseExt<CgiGetRtspUrlValue> & {\n cmd: \"GetRtspUrl\";\n };\n\nexport type CgiAbilityLeaf = {\n permit: number;\n ver: number;\n};\n\nexport type CgiAbilityChn = {\n aiTrack?: CgiAbilityLeaf;\n aiTrackDogCat?: CgiAbilityLeaf;\n alarmAudio?: CgiAbilityLeaf;\n alarmIoIn?: CgiAbilityLeaf;\n alarmIoOut?: CgiAbilityLeaf;\n alarmMd?: CgiAbilityLeaf;\n alarmRf?: CgiAbilityLeaf;\n batAnalysis?: CgiAbilityLeaf;\n battery?: CgiAbilityLeaf;\n cameraMode?: CgiAbilityLeaf;\n channelType?: CgiAbilityLeaf;\n customAudio?: CgiAbilityLeaf;\n disableAutoFocus?: CgiAbilityLeaf;\n enc?: CgiAbilityLeaf;\n floodLight?: CgiAbilityLeaf;\n ftp?: CgiAbilityLeaf;\n image?: CgiAbilityLeaf;\n indicatorLight?: CgiAbilityLeaf;\n isp?: CgiAbilityLeaf;\n isp3Dnr?: CgiAbilityLeaf;\n ispAntiFlick?: CgiAbilityLeaf;\n ispBackLight?: CgiAbilityLeaf;\n ispBright?: CgiAbilityLeaf;\n ispContrast?: CgiAbilityLeaf;\n ispDayNight?: CgiAbilityLeaf;\n ispExposureMode?: CgiAbilityLeaf;\n ispFlip?: CgiAbilityLeaf;\n ispHue?: CgiAbilityLeaf;\n ispMirror?: CgiAbilityLeaf;\n ispSatruation?: CgiAbilityLeaf;\n ispSharpen?: CgiAbilityLeaf;\n ispWhiteBalance?: CgiAbilityLeaf;\n ledControl?: CgiAbilityLeaf;\n lightType?: CgiAbilityLeaf;\n live?: CgiAbilityLeaf;\n mainEncType?: CgiAbilityLeaf;\n mask?: CgiAbilityLeaf;\n mdTriggerAudio?: CgiAbilityLeaf;\n mdTriggerRecord?: CgiAbilityLeaf;\n mdWithPir?: CgiAbilityLeaf;\n osd?: CgiAbilityLeaf;\n powerLed?: CgiAbilityLeaf;\n ptzCtrl?: CgiAbilityLeaf;\n ptzDirection?: CgiAbilityLeaf;\n ptzPatrol?: CgiAbilityLeaf;\n ptzPreset?: CgiAbilityLeaf;\n ptzTattern?: CgiAbilityLeaf;\n ptzType?: CgiAbilityLeaf;\n recCfg?: CgiAbilityLeaf;\n recDownload?: CgiAbilityLeaf;\n recReplay?: CgiAbilityLeaf;\n recSchedule?: CgiAbilityLeaf;\n shelterCfg?: CgiAbilityLeaf;\n snap?: CgiAbilityLeaf;\n supportAIDenoise?: CgiAbilityLeaf;\n supportAITrackLimit?: CgiAbilityLeaf;\n supportAITrackSchedule?: CgiAbilityLeaf;\n supportAfAlgorithmSwitch?: CgiAbilityLeaf;\n supportAi?: CgiAbilityLeaf;\n supportAiAnimal?: CgiAbilityLeaf;\n supportAiDetectConfig?: CgiAbilityLeaf;\n supportAiDogCat?: CgiAbilityLeaf;\n supportAiFace?: CgiAbilityLeaf;\n supportAiPackage?: CgiAbilityLeaf;\n supportAiPeople?: CgiAbilityLeaf;\n supportAiSensitivity?: CgiAbilityLeaf;\n supportAiStayTime?: CgiAbilityLeaf;\n supportAiTargetSize?: CgiAbilityLeaf;\n supportAiTrackClassify?: CgiAbilityLeaf;\n supportAiVehicle?: CgiAbilityLeaf;\n supportAllColors?: CgiAbilityLeaf;\n supportAoAdjust?: CgiAbilityLeaf;\n supportAudioAlarm?: CgiAbilityLeaf;\n supportAudioFileList?: CgiAbilityLeaf;\n supportAutoPt?: CgiAbilityLeaf;\n supportAutoReply?: CgiAbilityLeaf;\n supportAutoTrackStream?: CgiAbilityLeaf;\n supportBinoStitch?: CgiAbilityLeaf;\n supportDigitalZoom?: CgiAbilityLeaf;\n supportDingDongCtrl?: CgiAbilityLeaf;\n supportDoorbellLight?: CgiAbilityLeaf;\n supportDoorbellLightKeepOff?: CgiAbilityLeaf;\n supportEncoderSelect?: CgiAbilityLeaf;\n supportFLBrightness?: CgiAbilityLeaf;\n supportFLIntelligent?: CgiAbilityLeaf;\n supportFLKeepOn?: CgiAbilityLeaf;\n supportFLSchedule?: CgiAbilityLeaf;\n supportFLswitch?: CgiAbilityLeaf;\n supportFishEyeCfg?: CgiAbilityLeaf;\n supportFocus?: CgiAbilityLeaf;\n supportGop?: CgiAbilityLeaf;\n supportGuardPointImage?: CgiAbilityLeaf;\n supportImportExportImage?: CgiAbilityLeaf;\n supportIspBinningModeCfg?: CgiAbilityLeaf;\n supportLightAutoBrightness?: CgiAbilityLeaf;\n supportMd?: CgiAbilityLeaf;\n supportPt?: CgiAbilityLeaf;\n supportPtz3DLocation?: CgiAbilityLeaf;\n supportPtzCalibration?: CgiAbilityLeaf;\n supportPtzPresetImage?: CgiAbilityLeaf;\n supportPtzSpeed?: CgiAbilityLeaf;\n supportQuickReplyPlay?: CgiAbilityLeaf;\n supportThresholdAdjust?: CgiAbilityLeaf;\n supportVisitorLoudspeaker?: CgiAbilityLeaf;\n supportWLLightAlarm?: CgiAbilityLeaf;\n supportWebhook?: CgiAbilityLeaf;\n supportWhiteDark?: CgiAbilityLeaf;\n supportWiFi?: CgiAbilityLeaf;\n supportWiFiSdb?: CgiAbilityLeaf;\n supportZoom?: CgiAbilityLeaf;\n supportZoomAndFocusSliderCfg?: CgiAbilityLeaf;\n talk?: CgiAbilityLeaf;\n videoClip?: CgiAbilityLeaf;\n waterMark?: CgiAbilityLeaf;\n white_balance?: CgiAbilityLeaf;\n\n [key: string]: CgiAbilityLeaf | undefined;\n};\n\nexport type CgiAbility = {\n Ability: { abilityChn?: CgiAbilityChn[] } & Record<string, JsonValue>;\n};\n\nexport type CgiGetAbilityValue = CgiAbility;\n\nexport type CgiGetAbilityResponse =\n ReolinkCmdResponseExt<CgiGetAbilityValue> & {\n cmd: \"GetAbility\";\n };\n\nexport type CgiDevInfo = {\n B485?: number;\n IOInputNum?: number;\n IOOutputNum?: number;\n audioNum?: number;\n buildDay?: string;\n cfgVer?: string;\n channelNum?: number;\n detail?: string;\n diskNum?: number;\n exactType?: string;\n firmVer?: string;\n frameworkVer?: number;\n hardVer?: string;\n model?: string;\n name?: string;\n pakSuffix?: string;\n serial?: string;\n type?: string;\n wifi?: number;\n};\n\nexport type CgiGetDevInfoValue = {\n DevInfo: CgiDevInfo;\n};\n\nexport type CgiGetDevInfoResponse =\n ReolinkCmdResponseExt<CgiGetDevInfoValue> & {\n cmd: \"GetDevInfo\";\n };\n\nexport type CgiOsd = {\n channel: number;\n osdChannel?: number;\n osdTime?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetOsdValue = {\n Osd?: CgiOsd;\n} & Record<string, JsonValue>;\n\nexport type CgiSetOsdParam = {\n Osd: {\n channel: number;\n osdChannel?: number;\n osdTime?: number;\n };\n};\n\nexport type CgiWhiteLed = {\n channel: number;\n state?: number;\n bright?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiSetWhiteLedParam = {\n WhiteLed: CgiWhiteLed;\n};\n\nexport type CgiPirInfo = {\n channel: number;\n enable: number;\n} & Record<string, JsonValue>;\n\nexport type CgiSetPirInfoParam = {\n pirInfo: CgiPirInfo;\n};\n\nexport type CgiPtzPreset = {\n enable?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiAudioAlarmPlayParam =\n | ({ channel: number } & { alarm_mode: \"times\"; times: number })\n | ({ channel: number } & { alarm_mode: \"manul\"; manual_switch: number });\n\n// ── Isp (image colour / exposure) ──────────────────────────────────\n//\n// `Isp` is the modern Reolink endpoint for tunable colour pipeline\n// (brightness / contrast / saturation / sharpness / hue) plus\n// exposure / day-night / anti-flicker / white-balance. Field set\n// varies by model — capability flags (`ispBright`, `ispContrast`,\n// `ispDayNight`, …) under `CgiAbilityChn` advertise which knobs\n// the camera honours.\nexport type CgiIsp = {\n channel: number;\n bright?: number;\n contrast?: number;\n saturation?: number;\n sharpen?: number;\n hue?: number;\n antiFlicker?: string; // \"Outdoor\" | \"50HZ\" | \"60HZ\" | \"Off\"\n exposure?: string; // \"Auto\" | \"Manual\"\n dayNight?: string; // \"Color\" | \"Black&White\" | \"Auto\"\n backLight?: string; // \"BackLightControl\" | \"DynamicRangeControl\" | \"Off\"\n blueGain?: number;\n redGain?: number;\n whiteBalance?: string; // \"Auto\" | \"ManualDay\" | \"ManualNight\" | …\n mirroring?: number;\n flip?: number;\n rotation?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetIspValue = {\n Isp?: CgiIsp;\n} & Record<string, JsonValue>;\n\nexport type CgiSetIspParam = {\n Isp: CgiIsp;\n};\n\n// ── Image (legacy flip/mirror — distinct from Isp) ────────────────\nexport type CgiImage = {\n channel: number;\n bright?: number;\n contrast?: number;\n saturation?: number;\n hue?: number;\n sharpen?: number;\n mirroring?: number;\n flip?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetImageValue = {\n Image?: CgiImage;\n} & Record<string, JsonValue>;\n\nexport type CgiSetImageParam = {\n Image: CgiImage;\n};\n\n// ── AudioCfg (mute + volume on legacy firmwares) ──────────────────\nexport type CgiAudioCfg = {\n channel: number;\n /** 0 = unmuted, 1 = muted. */\n mute?: number;\n /** 0..100. */\n volume?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetAudioCfgValue = {\n AudioCfg?: CgiAudioCfg;\n} & Record<string, JsonValue>;\n\nexport type CgiSetAudioCfgParam = {\n AudioCfg: CgiAudioCfg;\n};\n\n// ── Enc (encoder / streaming profiles — SET counterpart) ──────────\n//\n// `CgiEnc` and `CgiEncStream` are already exported above for the\n// existing `GetEnc`. The SET param wraps the same structure under\n// the `Enc` root. Operator-relevant fields: `bitRate`, `frameRate`,\n// `gop`, `size` (resolution string), `vType` (`h264`/`h265`),\n// `profile`. Sub-stream / main-stream switch by passing the matching\n// inner key. Cameras typically reject mid-flight changes to\n// `vType` — set when streams are paused.\nexport type CgiSetEncParam = {\n Enc: CgiEnc;\n};\n\n// ── MdAlarm (motion-detection sensitivity grid) ───────────────────\n//\n// Reolink's modern MdAlarm encodes the sensitivity grid as a\n// `scope.table` string (`'1' | '0'` per row × col cell) and per-\n// schedule sensitivity entries under `sens[]`. For most operator\n// flows we only flip `enable` + bump a single `sensitivity` value —\n// callers can read the current shape via `GetMdAlarm` and patch\n// what they need.\nexport type CgiMdAlarmSens = {\n id?: number;\n beginHour?: number;\n beginMin?: number;\n endHour?: number;\n endMin?: number;\n sensitivity?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiMdAlarmScope = {\n cols?: number;\n rows?: number;\n table?: string;\n} & Record<string, JsonValue>;\n\nexport type CgiMdAlarm = {\n channel: number;\n type?: string;\n enable?: number;\n scope?: CgiMdAlarmScope;\n sens?: CgiMdAlarmSens[];\n} & Record<string, JsonValue>;\n\nexport type CgiGetMdAlarmValue = {\n MdAlarm?: CgiMdAlarm;\n} & Record<string, JsonValue>;\n\nexport type CgiSetMdAlarmParam = {\n MdAlarm: CgiMdAlarm;\n};\n\n// ── IrLights (IR LED control) ─────────────────────────────────────\nexport type CgiIrLights = {\n channel: number;\n /** \"Auto\" | \"Off\" — modern firmwares; some legacy support \"On\". */\n state?: string;\n} & Record<string, JsonValue>;\n\nexport type CgiGetIrLightsValue = {\n IrLights?: CgiIrLights;\n} & Record<string, JsonValue>;\n\nexport type CgiSetIrLightsParam = {\n IrLights: CgiIrLights;\n};\n\n// ── AiCfg (smart detection enable + class filter) ─────────────────\nexport type CgiAiCfg = {\n channel: number;\n AiTrack?: number;\n smartTrack?: number;\n trackType?: Record<string, number>;\n} & Record<string, JsonValue>;\n\nexport type CgiGetAiCfgValue = {\n AiCfg?: CgiAiCfg;\n} & Record<string, JsonValue>;\n\nexport type CgiSetAiCfgParam = {\n AiCfg: CgiAiCfg;\n};\n\n// ── Mask (privacy mask zones) ─────────────────────────────────────\nexport type CgiMaskShelter = {\n enabled?: number;\n position?: { x?: number; y?: number; w?: number; h?: number };\n} & Record<string, JsonValue>;\n\nexport type CgiMask = {\n channel: number;\n enable?: number;\n shelterList?: CgiMaskShelter[];\n} & Record<string, JsonValue>;\n\nexport type CgiGetMaskValue = {\n Mask?: CgiMask;\n} & Record<string, JsonValue>;\n\nexport type CgiSetMaskParam = {\n Mask: CgiMask;\n};\n\n// ── AudioNoise (input noise reduction) ────────────────────────────\nexport type CgiAudioNoise = {\n channel: number;\n enable?: number;\n /** 0 = disabled, 1..N = strength tier (model-specific). */\n level?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetAudioNoiseValue = {\n AudioNoise?: CgiAudioNoise;\n} & Record<string, JsonValue>;\n\nexport type CgiSetAudioNoiseParam = {\n AudioNoise: CgiAudioNoise;\n};\n\n// ── Rec / RecV20 (recording schedule) ─────────────────────────────\nexport type CgiRecSchedule = {\n channel: number;\n enable?: number;\n /** 7×24 weekly schedule mask, \"1\"/\"0\" per slot — present on V20+. */\n table?: string;\n} & Record<string, JsonValue>;\n\nexport type CgiRec = {\n schedule?: CgiRecSchedule;\n scheduleEnable?: number;\n enable?: number;\n packTime?: string; // e.g. \"60 Minutes\"\n postRec?: string; // e.g. \"15 Seconds\"\n} & Record<string, JsonValue>;\n\nexport type CgiGetRecValue = {\n Rec?: CgiRec;\n} & Record<string, JsonValue>;\n\nexport type CgiSetRecParam = {\n Rec: CgiRec;\n};\n\n// ── Email (SMTP alert config) ─────────────────────────────────────\nexport type CgiEmail = {\n schedule?: { channel?: number; enable?: number; table?: string };\n scheduleEnable?: number;\n enable?: number;\n smtpServer?: string;\n smtpPort?: number;\n userName?: string;\n password?: string;\n addr1?: string;\n addr2?: string;\n addr3?: string;\n ssl?: number;\n attachment?: number;\n interval?: string;\n textType?: string;\n subject?: string;\n content?: string;\n} & Record<string, JsonValue>;\n\nexport type CgiGetEmailValue = {\n Email?: CgiEmail;\n} & Record<string, JsonValue>;\n\nexport type CgiSetEmailParam = {\n Email: CgiEmail;\n};\n\n// ── Push (Reolink-cloud push notifications) ───────────────────────\nexport type CgiPush = {\n schedule?: { channel?: number; enable?: number; table?: string };\n scheduleEnable?: number;\n enable?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetPushValue = {\n Push?: CgiPush;\n} & Record<string, JsonValue>;\n\nexport type CgiSetPushParam = {\n Push: CgiPush;\n};\n\n// ── AudioAlarm (siren-on-event) ───────────────────────────────────\nexport type CgiAudioAlarm = {\n schedule?: { channel?: number; enable?: number; table?: string };\n scheduleEnable?: number;\n enable?: number;\n /** Built-in audio id; varies by camera. Use AudioAlarmPlay for\n * one-shot test playback. */\n audioId?: number;\n /** Times to repeat the audio per trigger. */\n alarmTimes?: number;\n audioVolume?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetAudioAlarmValue = {\n Audio?: CgiAudioAlarm;\n} & Record<string, JsonValue>;\n\nexport type CgiSetAudioAlarmParam = {\n Audio: CgiAudioAlarm;\n};\n\n// ── AutoFocus (PTZ AF disable / re-arm) ───────────────────────────\nexport type CgiAutoFocus = {\n channel: number;\n /** 0 = enabled (default), 1 = disabled. Reolink names it `disable`\n * rather than `enable` because AF is on by default. */\n disable?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetAutoFocusValue = {\n AutoFocus?: CgiAutoFocus;\n} & Record<string, JsonValue>;\n\nexport type CgiSetAutoFocusParam = {\n AutoFocus: CgiAutoFocus;\n};\n\n// ── AiAlarm (per-class smart-detection thresholds) ────────────────\n//\n// `ai_type` is one of `people` / `vehicle` / `dog_cat` / `face` /\n// `package` (depends on the camera's AI capabilities). `sensitivity`\n// is 0..100; `stayTime` is how long an object must remain in frame\n// before triggering (seconds).\nexport type CgiAiAlarm = {\n channel: number;\n ai_type: string;\n sensitivity?: number;\n stayTime?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiGetAiAlarmValue = {\n AiAlarm?: CgiAiAlarm;\n} & Record<string, JsonValue>;\n\nexport type CgiSetAiAlarmParam = {\n AiAlarm: CgiAiAlarm;\n};\n\nexport type CgiNetPort = Record<string, JsonValue>;\n\nexport type CgiBattery = {\n batteryPercent?: number;\n} & Record<string, JsonValue>;\n\nexport type CgiDeviceInfoEntries = [\n CgiGetChnTypeInfoResponse | undefined,\n CgiGetAiStateResponse | undefined,\n CgiGetEncResponse | undefined,\n];\n\nexport type DeviceInputData = {\n hasBattery: boolean;\n hasPirEvents: boolean;\n hasFloodlight: boolean;\n hasPtz: boolean;\n sleeping: boolean;\n};\n\nexport type EventsResponse = {\n motion: boolean;\n objects: string[];\n entries: Array<ReolinkCmdResponseExt<JsonValue> | undefined>;\n};\n\nexport type DeviceInfoResponse = {\n channelStatus?: CgiChannelStatusEntry;\n abilities?: CgiAbilityChn;\n ai?: CgiAiStateValue;\n channelInfo?: CgiChnTypeInfoValue;\n enc?: CgiEncValue;\n entries: CgiDeviceInfoEntries;\n};\n\nexport type BatteryInfoResponse = {\n batteryLevel: number;\n sleeping: boolean;\n entries: [CgiBattery | undefined, CgiChannelStatusEntry | undefined];\n};\n\nexport type DeviceStatusResponse = {\n floodlightEnabled?: boolean;\n pirEnabled?: boolean;\n ptzPresets?: CgiPtzPreset[];\n osd?: ReolinkCmdResponseExt<CgiGetOsdValue>;\n entries: Array<ReolinkCmdResponseExt<JsonValue>>;\n};\n\n// VOD (Video On Demand) types for hub/NVR recordings\nexport type VodSearchStatus = {\n year: number;\n mon: number;\n /** Bitmap string indicating which days of the month have recordings (e.g., \"1111111100000000000000000000000\") */\n table?: string;\n /** Legacy field - may not be present */\n day?: number;\n /** Legacy field - may not be present */\n month?: number;\n};\n\nexport type VodFile = {\n type: string;\n StartTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n EndTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n PlaybackTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n name: string;\n /** File size in bytes - API may return as string or number */\n size: number | string;\n};\n\nexport type VodSearchResult = {\n SearchResult?: {\n Status?: VodSearchStatus[];\n File?: VodFile[];\n };\n};\n\nexport type VodSearchResponse = ReolinkCmdResponseExt<VodSearchResult> & {\n cmd: \"Search\";\n};\n\ntype RecordingsCacheKey = string;\n\ntype RecordingsCacheEntry = {\n data: Array<RecordingFile>;\n expiresAt: number;\n};\n\n/**\n * Parameters for getVideoclips() recording search (CGI API).\n * Note: CGI API uses different parameters than Baichuan API.\n */\nexport interface CgiGetVideoclipsParams {\n /** Channel number (0-based). Required for NVR, optional for standalone cameras. */\n channel: number;\n /** Start date/time for search */\n start: Date;\n /** End date/time for search */\n end: Date;\n /** Stream type: \"main\" (default), \"sub\", \"autotrack_main\", \"autotrack_sub\", \"telephoto_main\", \"telephoto_sub\" */\n streamType?: string;\n /** For multifocal cameras: logical channel (0 or 1) */\n iLogicChannel?: number;\n /** If true, automatically search day-by-day when Status table is available (default: false) */\n autoSearchByDay?: boolean;\n /** If true, bypass cache and fetch fresh data (default: false) */\n bypassCache?: boolean;\n /**\n * If true, fetch streaming URLs for each recording.\n * This adds latency as it requires additional API calls.\n * Default: false.\n */\n fetchStreamUrls?: boolean;\n /** Stream URL type (only when fetchStreamUrls=true): \"FLV\" (default), \"RTMP\", \"Playback\" */\n streamUrlType?: \"FLV\" | \"RTMP\" | \"Playback\";\n}\n\n/**\n * Options for collecting NVR diagnostics.\n */\nexport interface CollectNvrDiagnosticsOptions {\n /** Logger for progress messages */\n logger: Logger;\n}\n\n/**\n * Parameters for getting VOD URL for playback, download, or streaming.\n */\nexport interface GetVodUrlParams {\n /** Request type: \"Playback\" (default), \"Download\", \"FLV\", \"RTMP\", \"NVR_DOWNLOAD\" */\n requestType?: \"Playback\" | \"Download\" | \"FLV\" | \"RTMP\" | \"NVR_DOWNLOAD\";\n /** Stream type for FLV/RTMP: \"main\" (default), \"sub\" */\n streamType?: string;\n /** Video stream type (alias for streamType, used when preparing from VodFile) */\n videoStreamType?: string;\n /** Start time string for Playback/Download */\n startTime?: string;\n /** Start time as JavaScript Date (for NVR preparation, will be converted to Reolink format) */\n startTimeObj?: Date;\n /** End time as JavaScript Date (for NVR preparation, will be converted to Reolink format) */\n endTimeObj?: Date;\n /** If true, automatically prepare file for NVR/Hub when possible (default: true for Playback/Download/NVR_DOWNLOAD) */\n prepare?: boolean;\n /** Seek position in seconds (for FLV) */\n seek?: number;\n}\n\nexport class ReolinkCgiApi {\n readonly client: ReolinkHttpClient;\n private logger: Logger = console;\n private debugConfig: DebugConfig = {\n general: false,\n debugRtsp: false,\n traceNativeStream: false,\n traceRecordings: false,\n traceEvents: false,\n traceTalk: false,\n dumpEnabled: false,\n dumpDir: \"\",\n dumpBcMedia: false,\n dumpNals: false,\n };\n\n // Recordings cache: key -> { data, expiresAt }\n // Unified cache for both NVR and Device recordings (always enriched)\n private recordingsCache = new Map<RecordingsCacheKey, RecordingsCacheEntry>();\n\n // Some devices (notably some Hub firmwares) do not support the `NvrDownload` prepare command.\n // Cache the capability to avoid repeated failing calls.\n private nvrDownloadPrepareSupport: \"unknown\" | \"supported\" | \"unsupported\" =\n \"unknown\";\n\n // Default cache TTL: 5 minutes\n private recordingsCacheTtlMs = 5 * 60 * 1000;\n\n constructor(\n opts: ReolinkHttpClientOptions & {\n logger?: Logger;\n debugConfig?: DebugConfig;\n },\n ) {\n this.client = new ReolinkHttpClient(opts);\n if (opts.logger) {\n this.logger = opts.logger;\n }\n if (opts.debugConfig) {\n this.debugConfig = opts.debugConfig;\n }\n }\n\n /**\n * Set logger for debug output\n */\n setLogger(logger: Logger): void {\n this.logger = logger;\n }\n\n /**\n * Set debug config for trace logging\n */\n setDebugConfig(debugConfig: DebugConfig): void {\n this.debugConfig = debugConfig;\n }\n\n /**\n * Set recordings cache TTL (time to live) in milliseconds.\n * Default: 5 minutes (300000 ms)\n */\n setRecordingsCacheTtl(ttlMs: number): void {\n this.recordingsCacheTtlMs = Math.max(0, ttlMs);\n }\n\n /**\n * Clear all recordings cache entries.\n */\n clearRecordingsCache(): void {\n this.recordingsCache.clear();\n }\n\n /**\n * Get recordings cache statistics.\n */\n getRecordingsCacheStats(): {\n size: number;\n ttlMs: number;\n entries: Array<{ key: string; expiresAt: number; expired: boolean }>;\n } {\n const now = Date.now();\n const entries: Array<{ key: string; expiresAt: number; expired: boolean }> =\n [];\n for (const [key, entry] of this.recordingsCache.entries()) {\n entries.push({\n key,\n expiresAt: entry.expiresAt,\n expired: entry.expiresAt < now,\n });\n }\n return {\n size: this.recordingsCache.size,\n ttlMs: this.recordingsCacheTtlMs,\n entries,\n };\n }\n\n /**\n * Clear expired recordings cache entries.\n */\n private cleanRecordingsCache(): void {\n const now = Date.now();\n for (const [key, entry] of this.recordingsCache.entries()) {\n if (entry.expiresAt < now) {\n this.recordingsCache.delete(key);\n }\n }\n }\n\n /**\n * Generate cache key for NVR recordings search.\n */\n private getNvrRecordingsCacheKey(\n channel: number,\n startTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n endTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n streamType: string,\n iLogicChannel: number,\n autoSearchByDay: boolean,\n ): RecordingsCacheKey {\n return `nvr:${channel}:${startTime.year}-${startTime.mon}-${startTime.day}-${startTime.hour}-${startTime.min}-${startTime.sec}:${endTime.year}-${endTime.mon}-${endTime.day}-${endTime.hour}-${endTime.min}-${endTime.sec}:${streamType}:${iLogicChannel}:${autoSearchByDay ? 1 : 0}`;\n }\n\n async login(): Promise<void> {\n await this.client.login();\n }\n\n async logout(): Promise<void> {\n await this.client.logout();\n }\n\n async call<TValue = JsonValue, TParam = JsonValue>(\n cmd: string,\n param?: TParam,\n action = 0,\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n // Avoid `param: undefined` with exactOptionalPropertyTypes\n if (param === undefined)\n return await this.client.call<TValue, TParam>(cmd, { action });\n return await this.client.call<TValue, TParam>(cmd, { action, param });\n }\n\n async callMany<TValue = JsonValue>(\n cmds: ReolinkCmdRequest[],\n ): Promise<ReolinkCmdResponse<TValue>[]> {\n return await this.client.callMany<TValue>(cmds);\n }\n\n private static findFirstRtspUrl(v: unknown, depth = 0): string | undefined {\n if (depth > 6) return undefined;\n if (typeof v === \"string\") {\n const s = v.trim();\n return s.startsWith(\"rtsp://\") ? s : undefined;\n }\n if (!v || typeof v !== \"object\") return undefined;\n if (Array.isArray(v)) {\n for (const item of v) {\n const found = ReolinkCgiApi.findFirstRtspUrl(item, depth + 1);\n if (found) return found;\n }\n return undefined;\n }\n\n const obj = v as Record<string, unknown>;\n const directKeys = [\"rtspUrl\", \"RtspUrl\", \"rtsp\", \"url\"];\n for (const k of directKeys) {\n const found = ReolinkCgiApi.findFirstRtspUrl(obj[k], depth + 1);\n if (found) return found;\n }\n for (const k of Object.keys(obj)) {\n const found = ReolinkCgiApi.findFirstRtspUrl(obj[k], depth + 1);\n if (found) return found;\n }\n return undefined;\n }\n\n // Common wrappers\n async GetDevInfo(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetDevInfoValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetDevInfo\", param);\n }\n\n /**\n * CGI equivalent of Baichuan `getInfo()`.\n *\n * Uses `GetDevInfo` and returns a minimal normalized map compatible with the Baichuan helper:\n * - type\n * - hardwareVersion\n * - firmwareVersion\n * - itemNo\n * - serialNumber\n * - name\n */\n async getInfo(\n channel?: number,\n options?: {\n /** List of normalized fields to return. Defaults to the canonical minimal set used by Baichuan getInfo(). */\n tags?: ReolinkDeviceInfoTag[];\n },\n ): Promise<Partial<ReolinkDeviceInfo>> {\n const rsp = await this.GetDevInfo(channel);\n const devInfo = (rsp as any)?.[0]?.value?.DevInfo as CgiDevInfo | undefined;\n\n const normalized: Partial<ReolinkDeviceInfo> = {};\n const type = (devInfo?.type ?? devInfo?.model ?? devInfo?.exactType) as\n | string\n | undefined;\n const itemNo = (devInfo?.exactType ?? devInfo?.model ?? devInfo?.detail) as\n | string\n | undefined;\n if (typeof type === \"string\") normalized.type = type;\n if (typeof devInfo?.hardVer === \"string\")\n normalized.hardwareVersion = devInfo.hardVer;\n if (typeof devInfo?.firmVer === \"string\")\n normalized.firmwareVersion = devInfo.firmVer;\n if (typeof itemNo === \"string\") normalized.itemNo = itemNo;\n if (typeof devInfo?.serial === \"string\")\n normalized.serialNumber = devInfo.serial;\n if (typeof devInfo?.name === \"string\") normalized.name = devInfo.name;\n\n const tags: ReolinkDeviceInfoTag[] = options?.tags?.length\n ? options.tags\n : ([\n \"type\",\n \"hardwareVersion\",\n \"firmwareVersion\",\n \"itemNo\",\n \"serialNumber\",\n \"name\",\n ] satisfies ReolinkDeviceInfoTag[]);\n\n const out: Partial<ReolinkDeviceInfo> = {};\n for (const t of tags) {\n const v = normalized[t];\n if (typeof v === \"string\") out[t] = v;\n }\n return out;\n }\n\n async GetChnTypeInfo(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetChnTypeInfo\", param);\n }\n\n async GetChannelstatus(): Promise<\n Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>\n > {\n return await this.call(\"GetChannelstatus\", undefined, 0);\n }\n\n async GetLocalLink(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetLocalLink\", param);\n }\n\n async GetWifiSignal(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetWifiSignal\", param);\n }\n\n async GetOsd(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetOsdValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetOsd\", param, 1);\n }\n\n async SetOsd(\n osd: CgiSetOsdParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetOsd\", osd, 0);\n }\n\n async GetEnc(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiEncValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetEnc\", param, 1);\n }\n\n /**\n * Return an RTSP URL for the given channel (NVR-side).\n * Uses the exact request body shape:\n * `[{\"cmd\":\"GetRtspUrl\",\"action\":0,\"param\":{\"channel\":<channel>}}]`.\n */\n async GetRtspUrl(channel: number): Promise<Array<CgiGetRtspUrlResponse>> {\n const body: ReolinkCmdRequest[] = [\n { cmd: \"GetRtspUrl\", action: 0, param: { channel } },\n ];\n return (await this.callMany(body)) as Array<CgiGetRtspUrlResponse>;\n }\n\n /** Convenience helper: extracts the first `rtsp://...` from GetRtspUrl response. */\n async getRtspUrl(channel: number): Promise<string> {\n const rsp = await this.GetRtspUrl(channel);\n const value = rsp?.[0]?.value;\n const url = ReolinkCgiApi.findFirstRtspUrl(value);\n if (!url) {\n throw new Error(\n `GetRtspUrl: RTSP URL not found in response (channel=${channel})`,\n );\n }\n return url;\n }\n\n async GetAiState(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiAiStateValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetAiState\", param, 0);\n }\n\n async GetMdState(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetMdState\", param, 0);\n }\n\n async GetEvents(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetEvents\", param, 0);\n }\n\n async GetBatteryInfo(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetBatteryInfo\", param, 0);\n }\n\n async GetWhiteLed(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetWhiteLed\", param, 0);\n }\n\n async SetWhiteLed(\n whiteLed: CgiSetWhiteLedParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetWhiteLed\", whiteLed, 0);\n }\n\n async GetPirInfo(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetPirInfo\", param, 0);\n }\n\n async SetPirInfo(\n pirInfo: CgiSetPirInfoParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetPirInfo\", pirInfo, 0);\n }\n\n async GetPtzPreset(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetPtzPreset\", param, 1);\n }\n\n // ── Isp / Image (colour, flip, day-night, exposure) ──────────────\n\n async GetIsp(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetIspValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetIsp\", param, 1);\n }\n\n async SetIsp(\n isp: CgiSetIspParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetIsp\", isp, 0);\n }\n\n async GetImage(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetImageValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetImage\", param, 1);\n }\n\n async SetImage(\n image: CgiSetImageParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetImage\", image, 0);\n }\n\n // ── AudioCfg (mute / volume) ─────────────────────────────────────\n\n async GetAudioCfg(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetAudioCfgValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetAudioCfg\", param, 1);\n }\n\n async SetAudioCfg(\n audio: CgiSetAudioCfgParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetAudioCfg\", audio, 0);\n }\n\n // ── Enc setter (Get already exists above) ────────────────────────\n\n async SetEnc(\n enc: CgiSetEncParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetEnc\", enc, 0);\n }\n\n // ── MdAlarm (motion detection sensitivity / regions) ─────────────\n\n async GetMdAlarm(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetMdAlarmValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetMdAlarm\", param, 1);\n }\n\n async SetMdAlarm(\n md: CgiSetMdAlarmParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetMdAlarm\", md, 0);\n }\n\n // ── IrLights ─────────────────────────────────────────────────────\n\n async GetIrLights(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetIrLightsValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetIrLights\", param, 1);\n }\n\n async SetIrLights(\n ir: CgiSetIrLightsParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetIrLights\", ir, 0);\n }\n\n // ── AiCfg (smart-detection enable + class filter) ────────────────\n\n async GetAiCfg(\n channel?: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetAiCfgValue>>> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"GetAiCfg\", param, 1);\n }\n\n async SetAiCfg(\n ai: CgiSetAiCfgParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetAiCfg\", ai, 0);\n }\n\n // ── Mask (privacy-mask zones) ────────────────────────────────────\n\n async GetMask(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetMaskValue>>> {\n return await this.call(\"GetMask\", { channel }, 1);\n }\n\n async SetMask(\n mask: CgiSetMaskParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetMask\", mask, 0);\n }\n\n // ── AudioNoise (input noise reduction) ───────────────────────────\n\n async GetAudioNoise(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetAudioNoiseValue>>> {\n return await this.call(\"GetAudioNoise\", { channel }, 1);\n }\n\n async SetAudioNoise(\n noise: CgiSetAudioNoiseParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetAudioNoise\", noise, 0);\n }\n\n // ── Rec / RecV20 (recording schedule) ────────────────────────────\n\n async GetRec(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetRecValue>>> {\n return await this.call(\"GetRec\", { channel }, 1);\n }\n\n async SetRec(\n rec: CgiSetRecParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetRec\", rec, 0);\n }\n\n /** Newer firmwares advertise `GetRecV20` / `SetRecV20` with the\n * weekly-schedule `table` field. Same payload shape as `Rec`. */\n async GetRecV20(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetRecValue>>> {\n return await this.call(\"GetRecV20\", { channel }, 1);\n }\n\n async SetRecV20(\n rec: CgiSetRecParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetRecV20\", rec, 0);\n }\n\n // ── Email (SMTP alert) ───────────────────────────────────────────\n\n async GetEmail(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetEmailValue>>> {\n return await this.call(\"GetEmail\", { channel }, 1);\n }\n\n async SetEmail(\n email: CgiSetEmailParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetEmail\", email, 0);\n }\n\n /** V20 variant on newer firmwares with weekly-schedule `table`. */\n async GetEmailV20(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetEmailValue>>> {\n return await this.call(\"GetEmailV20\", { channel }, 1);\n }\n\n async SetEmailV20(\n email: CgiSetEmailParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetEmailV20\", email, 0);\n }\n\n // ── Push (Reolink-cloud push notifications) ──────────────────────\n\n async GetPush(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetPushValue>>> {\n return await this.call(\"GetPush\", { channel }, 1);\n }\n\n async SetPush(\n push: CgiSetPushParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetPush\", push, 0);\n }\n\n async GetPushV20(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetPushValue>>> {\n return await this.call(\"GetPushV20\", { channel }, 1);\n }\n\n async SetPushV20(\n push: CgiSetPushParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetPushV20\", push, 0);\n }\n\n // ── AudioAlarm (siren-on-event) ──────────────────────────────────\n\n async GetAudioAlarm(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetAudioAlarmValue>>> {\n return await this.call(\"GetAudioAlarm\", { channel }, 1);\n }\n\n async SetAudioAlarm(\n audio: CgiSetAudioAlarmParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetAudioAlarm\", audio, 0);\n }\n\n async SetAudioAlarmV20(\n audio: CgiSetAudioAlarmParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetAudioAlarmV20\", audio, 0);\n }\n\n // ── AutoFocus (PTZ AF) ───────────────────────────────────────────\n\n async GetAutoFocus(\n channel: number,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetAutoFocusValue>>> {\n return await this.call(\"GetAutoFocus\", { channel }, 1);\n }\n\n async SetAutoFocus(\n af: CgiSetAutoFocusParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetAutoFocus\", af, 0);\n }\n\n // ── AiAlarm (per-class smart-detection thresholds) ───────────────\n\n async GetAiAlarm(\n channel: number,\n aiType: string,\n ): Promise<Array<ReolinkCmdResponseExt<CgiGetAiAlarmValue>>> {\n return await this.call(\"GetAiAlarm\", { channel, ai_type: aiType }, 1);\n }\n\n async SetAiAlarm(\n ai: CgiSetAiAlarmParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetAiAlarm\", ai, 0);\n }\n\n async GetAudioAlarmV20(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n // NOTE: action stays at 0 here for backwards compat with existing\n // callers that depend on this exact wire shape — newer code that\n // wants the typed shape should use `GetAudioAlarm(channel)` (action 1).\n return await this.call(\"GetAudioAlarmV20\", param, 0);\n }\n\n async AudioAlarmPlay(\n params: CgiAudioAlarmPlayParam,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"AudioAlarmPlay\", params, 0);\n }\n\n async GetNetPort(): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"GetNetPort\", {});\n }\n\n async SetNetPort(\n netPort: CgiNetPort,\n ): Promise<Array<ReolinkCmdResponseExt<JsonValue>>> {\n return await this.call(\"SetNetPort\", { NetPort: netPort });\n }\n\n async Reboot(channel?: number): Promise<ReolinkCmdResponse[]> {\n const param = channel == null ? {} : { channel };\n return await this.call(\"Reboot\", param);\n }\n\n async GetAbility(): Promise<Array<ReolinkCmdResponseExt<CgiAbility>>> {\n const username = this.client.getUsername();\n return await this.call(\"GetAbility\", {\n User: {\n userName: username,\n },\n });\n }\n\n // --------------------\n // High-level helpers (batch-oriented)\n // --------------------\n\n /** Returns the list of channels that have a non-empty UID (typically the connected cameras on NVR/Home Hub). */\n async getChannels(options?: { useChannelNumFallback?: boolean }): Promise<{\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n }> {\n const channelsResponse = await this.GetChannelstatus();\n const status = channelsResponse?.[0]?.value?.status;\n let channels = (status ?? [])\n .filter((s) => !!s?.uid)\n .map((s) => Number(s?.channel))\n .filter((n) => Number.isFinite(n));\n\n // Fallback for multi-focal cameras: if no channels found and fallback is enabled, use channelNum from GetDevInfo\n if (channels.length === 0 && options?.useChannelNumFallback) {\n try {\n const devInfoRsp = await this.GetDevInfo();\n const devInfo = (devInfoRsp as any)?.[0]?.value?.DevInfo as\n | CgiDevInfo\n | undefined;\n const channelNum = devInfo?.channelNum;\n if (channelNum != null && channelNum > 0) {\n channels = Array.from({ length: channelNum }, (_, i) => i);\n }\n } catch (error) {\n // Ignore errors when trying to get channelNum fallback\n }\n }\n\n return { channels, channelsResponse };\n }\n\n async getNvrInfo(): Promise<{\n abilities: CgiAbility | undefined;\n nvrData: CgiGetDevInfoValue | undefined;\n devInfo: CgiDevInfo | undefined;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n }> {\n const username = this.client.getUsername();\n const body: ReolinkCmdRequest[] = [\n { cmd: \"GetAbility\", action: 0, param: { User: { userName: username } } },\n { cmd: \"GetDevInfo\", action: 0, param: {} },\n ];\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n const abilities = response.find((item: any) => item?.cmd === \"GetAbility\")\n ?.value as CgiAbility | undefined;\n const nvrData = response.find((item: any) => item?.cmd === \"GetDevInfo\")\n ?.value as CgiGetDevInfoValue | undefined;\n const devInfo = nvrData?.DevInfo;\n\n return { abilities, nvrData, devInfo, response };\n }\n\n async getDevicesInfo(options?: { useChannelNumFallback?: boolean }): Promise<{\n devicesData: Record<number, DeviceInfoResponse>;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n requestBody: ReolinkCmdRequest[];\n }> {\n const { channels, channelsResponse } = await this.getChannels(options);\n\n const username = this.client.getUsername();\n\n const body: ReolinkCmdRequest[] = [];\n\n body.push({\n cmd: \"GetAbility\",\n action: 0,\n param: { User: { userName: username } },\n });\n\n for (const channel of channels) {\n body.push(\n { cmd: \"GetChnTypeInfo\", action: 0, param: { channel } },\n { cmd: \"GetAiState\", action: 0, param: { channel } },\n { cmd: \"GetEnc\", action: 1, param: { channel } },\n );\n }\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n\n const abilities = (response[0] as CgiGetAbilityResponse | undefined)?.value;\n const abilitiesChn = abilities?.Ability?.abilityChn;\n\n const ret: Record<number, DeviceInfoResponse> = {};\n for (let i = 0; i < channels.length; i++) {\n const channel = channels[i]!;\n const base = 1 + i * 3;\n const chnInfoItem = response[base] as\n | CgiGetChnTypeInfoResponse\n | undefined;\n const aiItem = response[base + 1] as CgiGetAiStateResponse | undefined;\n const encItem = response[base + 2] as CgiGetEncResponse | undefined;\n\n const channelStatus = channelsResponse?.[0]?.value?.status?.find(\n (item) => item?.channel === channel,\n );\n\n const device: DeviceInfoResponse = {\n entries: [chnInfoItem, aiItem, encItem],\n };\n if (channelStatus) device.channelStatus = channelStatus;\n const perChannelAbilities = abilitiesChn?.[channel];\n if (perChannelAbilities) device.abilities = perChannelAbilities;\n\n if (!(chnInfoItem as any)?.error)\n device.channelInfo = (chnInfoItem as any)?.value as CgiChnTypeInfoValue;\n if (!(aiItem as any)?.error)\n device.ai = (aiItem as any)?.value as CgiAiStateValue;\n if (!(encItem as any)?.error)\n device.enc = (encItem as any)?.value as CgiEncValue;\n\n ret[channel] = device;\n }\n\n return {\n devicesData: ret,\n response,\n channels,\n channelsResponse,\n requestBody: body,\n };\n }\n\n async getAllChannelsEvents(options?: {\n useChannelNumFallback?: boolean;\n }): Promise<{\n parsed: Record<number, EventsResponse>;\n response: ReolinkCmdResponse[];\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n requestBody: ReolinkCmdRequest[];\n }> {\n const { channels, channelsResponse } = await this.getChannels(options);\n\n // Always call all relevant endpoints per channel and merge.\n const body: ReolinkCmdRequest[] = [];\n const index: Record<\n number,\n { events?: number; motion?: number; ai?: number }\n > = {};\n\n for (const channel of channels) {\n index[channel] = {};\n body.push({ cmd: \"GetEvents\", action: 0, param: { channel } });\n index[channel].events = body.length - 1;\n body.push({ cmd: \"GetMdState\", action: 0, param: { channel } });\n index[channel].motion = body.length - 1;\n body.push({ cmd: \"GetAiState\", action: 0, param: { channel } });\n index[channel].ai = body.length - 1;\n }\n\n const response = await this.callMany(body);\n\n const processDetections = (aiResponse: any): string[] => {\n const classes: string[] = [];\n for (const key of Object.keys(aiResponse ?? {})) {\n if (key === \"channel\") continue;\n const alarmState = aiResponse?.[key]?.alarm_state;\n if (alarmState) classes.push(key);\n }\n return classes;\n };\n\n const parsed: Record<number, EventsResponse> = {};\n for (const channel of channels) {\n const { events, motion, ai } = index[channel] ?? {};\n const eventsEntry = events != null ? response[events] : undefined;\n const motionEntry = motion != null ? response[motion] : undefined;\n const aiEntry = ai != null ? response[ai] : undefined;\n\n const classes = new Set<string>();\n for (const c of processDetections((aiEntry as any)?.value))\n classes.add(c);\n for (const c of processDetections(\n (eventsEntry as any)?.value?.ai ?? (eventsEntry as any)?.value,\n ))\n classes.add(c);\n\n const list = Array.from(classes);\n const objects = list.filter((cl) => cl !== \"other\");\n const hasMotion = !!(motionEntry as any)?.value?.state || list.length > 0;\n\n parsed[channel] = {\n motion: hasMotion,\n objects,\n entries: [eventsEntry as any, motionEntry as any, aiEntry as any],\n };\n }\n\n return { parsed, response, channels, channelsResponse, requestBody: body };\n }\n\n async getAllChannelsBatteryInfo(options?: {\n useChannelNumFallback?: boolean;\n }): Promise<{\n batteryInfoData: Record<number, BatteryInfoResponse>;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n channels: number[];\n channelsResponse: Array<ReolinkCmdResponseExt<CgiGetChannelstatusValue>>;\n requestBody: ReolinkCmdRequest[];\n }> {\n const { channels, channelsResponse } = await this.getChannels(options);\n\n // Always call battery info for every channel and merge with Channelstatus.\n const body: ReolinkCmdRequest[] = [{ cmd: \"GetChannelstatus\" }];\n const index: Record<number, number> = {};\n for (const channel of channels) {\n body.push({ cmd: \"GetBatteryInfo\", action: 0, param: { channel } });\n index[channel] = body.length - 1;\n }\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n const channelStatusData = response[0];\n\n const batteryInfoData: Record<number, BatteryInfoResponse> = {};\n for (const channel of channels) {\n const batteryInfoEntry = ((response[index[channel]!] as any)?.value\n ?.Battery ?? undefined) as CgiBattery | undefined;\n const channelStatusEntry = (\n channelStatusData as any\n )?.value?.status?.find((elem: any) => elem?.channel === channel) as\n | CgiChannelStatusEntry\n | undefined;\n batteryInfoData[channel] = {\n entries: [batteryInfoEntry, channelStatusEntry],\n batteryLevel: Number(batteryInfoEntry?.batteryPercent ?? 0),\n sleeping: channelStatusEntry?.sleep === 1,\n };\n }\n\n return {\n batteryInfoData,\n response,\n channels,\n channelsResponse,\n requestBody: body,\n };\n }\n\n async getStatusInfo(channelsMap: Map<number, DeviceInputData>): Promise<{\n deviceStatusData: Record<number, DeviceStatusResponse>;\n response: Array<ReolinkCmdResponseExt<JsonValue>>;\n }> {\n const body: ReolinkCmdRequest[] = [];\n const index: Record<\n number,\n { osd?: number; floodlight?: number; pir?: number; presets?: number }\n > = {};\n\n for (const [channel, info] of channelsMap.entries()) {\n index[channel] = {};\n if (info.sleeping) continue;\n\n body.push({ cmd: \"GetOsd\", action: 1, param: { channel } });\n index[channel].osd = body.length - 1;\n\n if (info.hasFloodlight) {\n body.push({ cmd: \"GetWhiteLed\", action: 0, param: { channel } });\n index[channel].floodlight = body.length - 1;\n }\n\n if (info.hasPirEvents) {\n body.push({ cmd: \"GetPirInfo\", action: 0, param: { channel } });\n index[channel].pir = body.length - 1;\n }\n\n if (info.hasPtz) {\n body.push({ cmd: \"GetPtzPreset\", action: 1, param: { channel } });\n index[channel].presets = body.length - 1;\n }\n }\n\n const response = (await this.callMany(body)) as Array<\n ReolinkCmdResponseExt<JsonValue>\n >;\n\n const deviceStatusData: Record<number, DeviceStatusResponse> = {};\n for (const [channel, info] of channelsMap.entries()) {\n const { osd, floodlight, pir, presets } = index[channel] ?? {};\n deviceStatusData[channel] = { entries: [] };\n\n if (osd != null) {\n const osdEntry = response[osd]!;\n deviceStatusData[channel].osd =\n osdEntry as ReolinkCmdResponseExt<CgiGetOsdValue>;\n deviceStatusData[channel].entries.push(osdEntry);\n }\n\n if (info.hasFloodlight && floodlight != null) {\n const floodlightEntry = response[floodlight]!;\n deviceStatusData[channel].floodlightEnabled =\n (floodlightEntry as any)?.value?.WhiteLed?.state === 1;\n deviceStatusData[channel].entries.push(floodlightEntry);\n }\n\n if (info.hasPirEvents && pir != null) {\n const pirEntry = response[pir]!;\n deviceStatusData[channel].pirEnabled =\n (pirEntry as any)?.value?.pirInfo?.enable === 1;\n deviceStatusData[channel].entries.push(pirEntry);\n }\n\n if (info.hasPtz && presets != null) {\n const ptzPresetsEntry = response[presets]!;\n const list = (ptzPresetsEntry as any)?.value?.PtzPreset;\n deviceStatusData[channel].ptzPresets = Array.isArray(list)\n ? (list.filter((p: any) => p?.enable === 1) as CgiPtzPreset[])\n : [];\n deviceStatusData[channel].entries.push(ptzPresetsEntry);\n }\n }\n\n return { deviceStatusData, response };\n }\n\n /** Convenience wrapper returning raw OSD response for a channel. */\n async getOsd(\n channel: number,\n ): Promise<ReolinkCmdResponseExt<CgiGetOsdValue> | undefined> {\n const rsp = await this.GetOsd(channel);\n return rsp?.[0];\n }\n\n /** Set channel OSD. Accepts either a full `Osd` object or a minimal `{ Osd: ... }` payload. */\n async setOsd(channel: number, osd: any): Promise<void> {\n const valueOsd = osd?.value?.Osd ?? osd?.Osd;\n const osdChannel = valueOsd?.osdChannel ?? osd?.osdChannel;\n const osdTime = valueOsd?.osdTime ?? osd?.osdTime;\n\n const payload = {\n Osd: {\n channel,\n osdChannel,\n osdTime,\n },\n };\n\n await this.call(\"SetOsd\", payload, 0);\n }\n\n async getEncoderConfiguration(channel: number): Promise<CgiEnc | undefined> {\n const rsp = await this.GetEnc(channel);\n return (rsp as any)?.[0]?.value?.Enc as CgiEnc | undefined;\n }\n\n /** CGI snapshot via `cmd=Snap` (binary JPEG). */\n async jpegSnapshot(\n channel: number,\n opts?: {\n timeoutMs?: number;\n snapType?: \"main\" | \"sub\";\n iLogicChannel?: number;\n },\n ): Promise<Buffer> {\n return await this.client.snap(channel, {\n ...(opts?.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n ...(opts?.snapType !== undefined ? { snapType: opts.snapType } : {}),\n ...(opts?.iLogicChannel !== undefined\n ? { iLogicChannel: opts.iLogicChannel }\n : {}),\n });\n }\n\n async getSiren(channel: number): Promise<{ enabled: boolean }> {\n const rsp = await this.GetAudioAlarmV20(channel);\n return { enabled: (rsp as any)?.[0]?.value?.Audio?.enable === 1 };\n }\n\n async setSiren(\n channel: number,\n on: boolean,\n duration?: number,\n ): Promise<{\n value: JsonValue | undefined;\n data: Array<ReolinkCmdResponseExt<JsonValue>>;\n }> {\n const params: CgiAudioAlarmPlayParam = duration\n ? { channel, alarm_mode: \"times\", times: duration }\n : { channel, alarm_mode: \"manul\", manual_switch: on ? 1 : 0 };\n\n const rsp = await this.AudioAlarmPlay(params);\n return {\n value: (rsp as any)?.[0]?.value ?? (rsp as any)?.value,\n data: rsp,\n };\n }\n\n async setWhiteLedState(\n channel: number,\n on?: boolean,\n brightness?: number,\n ): Promise<void> {\n const settings: any = { channel };\n if (on !== undefined) settings.state = on ? 1 : 0;\n if (brightness !== undefined) settings.bright = brightness;\n await this.SetWhiteLed({ WhiteLed: settings });\n }\n\n async getPirState(\n channel: number,\n ): Promise<{ enabled: boolean; state: CgiPirInfo | undefined }> {\n const rsp = await this.GetPirInfo(channel);\n const state = (rsp as any)?.[0]?.value?.pirInfo as CgiPirInfo | undefined;\n return { enabled: state?.enable === 1, state };\n }\n\n async setPirState(channel: number, on: boolean): Promise<void> {\n const current = await this.getPirState(channel);\n const newState = on ? 1 : 0;\n const currentEnable = (current?.state as any)?.enable;\n if (currentEnable === newState) return;\n\n const pirInfo = {\n ...(current?.state && typeof current.state === \"object\"\n ? current.state\n : {}),\n channel,\n enable: newState,\n };\n await this.SetPirInfo({ pirInfo });\n }\n\n async getLocalLink(channel: number): Promise<{\n activeLink: string | undefined;\n wifiSignal: number | undefined;\n isWifi: boolean;\n }> {\n const body: ReolinkCmdRequest[] = [\n { cmd: \"GetLocalLink\", action: 0, param: {} },\n { cmd: \"GetWifiSignal\", action: 0, param: { channel } },\n ];\n const rsp = await this.callMany(body);\n const activeLink = (rsp as any).find((e: any) => e?.cmd === \"GetLocalLink\")\n ?.value?.LocalLink?.activeLink as string | undefined;\n const wifiSignal = (rsp as any).find((e: any) => e?.cmd === \"GetWifiSignal\")\n ?.value?.wifiSignal as number | undefined;\n\n let isWifi = false;\n if (wifiSignal !== undefined) {\n isWifi = wifiSignal >= 0 && wifiSignal <= 4;\n }\n if (!isWifi && activeLink) {\n isWifi = activeLink !== \"LAN\";\n }\n\n return { activeLink, wifiSignal, isWifi };\n }\n\n /**\n * Comprehensive NVR/HUB diagnostics.\n * Collects and returns all available information about the NVR/HUB device and all its channels.\n * Automatically prints diagnostics after collection using the provided logger.\n *\n * @param options - Configuration object with logger property for progress messages\n * @returns Complete diagnostics data including NVR info, channels, and per-channel details\n */\n async collectNvrDiagnostics(\n options: CollectNvrDiagnosticsOptions,\n ): Promise<Record<string, unknown>> {\n const diagnostics = await collectNvrDiagnostics({\n cgi: this,\n logger: options.logger,\n });\n return diagnostics;\n }\n\n // --------------------\n // VOD (Video On Demand) methods for hub/NVR\n // --------------------\n\n /**\n * Get videoclips (recordings) via CGI Search API.\n * This command does NOT wake up battery cameras connected to the hub.\n *\n * Always returns enriched recording files with parsed metadata, detection flags, and timestamps.\n * Note: For best results, use autoSearchByDay=true to automatically search day-by-day when Status table is available.\n *\n * @example\n * ```typescript\n * // Search yesterday's recordings on channel 0\n * const yesterday = new Date();\n * yesterday.setDate(yesterday.getDate() - 1);\n * yesterday.setHours(0, 0, 0, 0);\n * const endOfDay = new Date(yesterday);\n * endOfDay.setHours(23, 59, 59, 999);\n *\n * const clips = await cgiApi.getVideoclips({\n * channel: 0,\n * start: yesterday,\n * end: endOfDay,\n * streamType: \"main\",\n * });\n * ```\n *\n * @param params - Search parameters\n * @returns Array of enriched recording files (RecordingFile[])\n */\n async getVideoclips(\n params: CgiGetVideoclipsParams,\n ): Promise<Array<RecordingFile>> {\n const { channel, start, end } = params;\n const streamType = params.streamType ?? \"main\";\n const iLogicChannel = params.iLogicChannel ?? 0;\n const autoSearchByDay = params.autoSearchByDay ?? false;\n const bypassCache = params.bypassCache ?? false;\n\n // Convert Date to Reolink time format\n let startTime = this.dateToReolinkTime(start);\n let endTime = this.dateToReolinkTime(end);\n\n // Sanitize end date: if it's midnight (00:00:00) of the next day, cap it to 23:59:59 of the same day as start\n // This ensures we search until the end of the requested day, not the beginning of the next day\n if (endTime.hour === 0 && endTime.min === 0 && endTime.sec === 0) {\n // Check if end is on a different day than start\n const startDateKey = `${startTime.year}-${startTime.mon}-${startTime.day}`;\n const endDateKey = `${endTime.year}-${endTime.mon}-${endTime.day}`;\n\n if (endDateKey !== startDateKey) {\n // End is on the next day at midnight, so cap it to the end of start day (23:59:59)\n endTime = {\n year: startTime.year,\n mon: startTime.mon,\n day: startTime.day,\n hour: 23,\n min: 59,\n sec: 59,\n };\n }\n }\n\n // Log date conversion for debugging (using both general debug and recordings trace)\n // debugLog(this.debugConfig, this.logger, \"getVideoclips\",\n // `Date conversion: start=${start.toISOString()} (local: ${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}-${String(start.getDate()).padStart(2, '0')} ${String(start.getHours()).padStart(2, '0')}:${String(start.getMinutes()).padStart(2, '0')}:${String(start.getSeconds()).padStart(2, '0')}) -> ReolinkTime=${JSON.stringify(startTime)}, ` +\n // `end=${end.toISOString()} (local: ${end.getFullYear()}-${String(end.getMonth() + 1).padStart(2, '0')}-${String(end.getDate()).padStart(2, '0')} ${String(end.getHours()).padStart(2, '0')}:${String(end.getMinutes()).padStart(2, '0')}:${String(end.getSeconds()).padStart(2, '0')}) -> ReolinkTime=${JSON.stringify(endTime)}`\n // );\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Date conversion: start=${start.toISOString()} -> ReolinkTime=${JSON.stringify(startTime)}, end=${end.toISOString()} -> ReolinkTime=${JSON.stringify(endTime)}`,\n );\n\n // Check cache first (unless bypassing)\n if (!bypassCache) {\n this.cleanRecordingsCache();\n const cacheKey = this.getNvrRecordingsCacheKey(\n channel,\n startTime,\n endTime,\n streamType,\n iLogicChannel,\n autoSearchByDay,\n );\n const cached = this.recordingsCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now()) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Cache hit: returning ${cached.data.length} cached enriched recordings`,\n );\n return cached.data;\n }\n }\n\n // If autoSearchByDay is enabled, first get status to find days with recordings\n if (autoSearchByDay) {\n const startMs = start.getTime();\n const endMs = end.getTime();\n\n const clampDateToWindow = (d: Date): Date => {\n const t = d.getTime();\n if (!Number.isFinite(t)) return d;\n if (t < startMs) return new Date(startMs);\n if (t > endMs) return new Date(endMs);\n return d;\n };\n\n const statusParam: any = {\n Search: {\n channel,\n onlyStatus: 1,\n streamType,\n StartTime: startTime,\n EndTime: endTime,\n },\n };\n if (iLogicChannel > 0) {\n statusParam.Search.iLogicChannel = iLogicChannel;\n }\n\n const statusResponse = await this.call<VodSearchResult>(\n \"Search\",\n statusParam,\n 0,\n );\n const allResults: Array<VodSearchResponse> = [];\n\n // Parse Status table to find days with recordings\n for (const statusResult of statusResponse) {\n if (\n statusResult.code === 0 &&\n statusResult.value?.SearchResult?.Status\n ) {\n for (const status of statusResult.value.SearchResult.Status) {\n if (status.table) {\n // Find days with recordings from bitmap\n const daysWithRecordings: number[] = [];\n for (let i = 0; i < status.table.length; i++) {\n if (status.table[i] === \"1\") {\n daysWithRecordings.push(i + 1); // Days are 1-indexed\n }\n }\n\n // Search each day individually\n for (const day of daysWithRecordings) {\n const year = status.year;\n const month = status.mon || status.month || 1;\n\n // IMPORTANT: Status table is typically per-month, and may include days outside the\n // requested window. Restrict queries to days that overlap [start,end], and clamp\n // the per-day query range to the window.\n const dayStartDate = new Date(year, month - 1, day, 0, 0, 0, 0);\n const dayEndDate = new Date(\n year,\n month - 1,\n day,\n 23,\n 59,\n 59,\n 999,\n );\n\n // Skip days completely outside the requested window.\n if (Number.isFinite(startMs) && Number.isFinite(endMs)) {\n if (\n dayEndDate.getTime() < startMs ||\n dayStartDate.getTime() > endMs\n )\n continue;\n }\n\n const queryStartDate = clampDateToWindow(\n dayStartDate.getTime() < startMs\n ? new Date(startMs)\n : dayStartDate,\n );\n const queryEndDate = clampDateToWindow(\n dayEndDate.getTime() > endMs ? new Date(endMs) : dayEndDate,\n );\n if (queryStartDate.getTime() > queryEndDate.getTime()) continue;\n\n const dayStart = this.dateToReolinkTime(queryStartDate);\n const dayEnd = this.dateToReolinkTime(queryEndDate);\n\n const dayParam: any = {\n Search: {\n channel,\n onlyStatus: 0,\n streamType,\n StartTime: dayStart,\n EndTime: dayEnd,\n },\n };\n if (iLogicChannel > 0) {\n dayParam.Search.iLogicChannel = iLogicChannel;\n }\n\n const dayResponse = await this.call<VodSearchResult>(\n \"Search\",\n dayParam,\n 0,\n );\n // Log raw API response for debugging\n if (dayResponse && dayResponse.length > 0) {\n const firstResult = dayResponse[0];\n if (\n firstResult &&\n firstResult.code === 0 &&\n firstResult.value?.SearchResult?.File\n ) {\n const files = firstResult.value.SearchResult.File;\n if (files.length > 0 && files[0]) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Raw API response for day ${day}/${month}/${year}: ${JSON.stringify(\n {\n fileCount: files.length,\n sampleFile: {\n name: files[0].name,\n type: files[0].type,\n size: files[0].size,\n StartTime: files[0].StartTime,\n EndTime: files[0].EndTime,\n },\n },\n )}`,\n );\n }\n }\n }\n allResults.push(...(dayResponse as Array<VodSearchResponse>));\n }\n }\n }\n }\n }\n\n if (allResults.length > 0) {\n // Collect all files from search results and enrich them\n const allFiles: VodFile[] = [];\n for (const res of allResults) {\n if (res.code === 0) {\n const searchResult = res.value?.SearchResult;\n if (searchResult?.File) {\n allFiles.push(...searchResult.File);\n }\n }\n }\n\n // Enrich all files\n const enriched: RecordingFile[] = [];\n const fetchStreamUrls = params.fetchStreamUrls === true;\n const streamUrlType = params.streamUrlType ?? \"FLV\";\n\n for (const vodFile of allFiles) {\n let streamUrl: string | undefined;\n\n // Optionally fetch streaming URL\n if (fetchStreamUrls) {\n try {\n const url = await this.getVodUrl(vodFile, channel, {\n requestType: streamUrlType,\n videoStreamType: streamType,\n prepare: true,\n });\n streamUrl = url;\n } catch (e) {\n // Silently ignore - not all recordings may have streaming URLs available\n }\n }\n\n enriched.push(this.enrichVodFile(vodFile, channel, streamUrl));\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Returning ${enriched.length} enriched recording files from autoSearchByDay`,\n );\n\n // Cache enriched results\n if (!bypassCache) {\n const cacheKey = this.getNvrRecordingsCacheKey(\n channel,\n startTime,\n endTime,\n streamType,\n iLogicChannel,\n autoSearchByDay,\n );\n this.recordingsCache.set(cacheKey, {\n data: enriched,\n expiresAt: Date.now() + this.recordingsCacheTtlMs,\n });\n }\n\n return enriched;\n }\n // Fall through to normal search if no status found\n }\n\n // Normal search: if range spans multiple days, split into day-by-day queries\n const allResults: Array<VodSearchResponse> = [];\n const dayRanges = this.generateDayRanges(startTime, endTime);\n\n if (dayRanges.length === 0) {\n // No valid range, return empty\n return [];\n }\n\n if (dayRanges.length === 1) {\n // Single day range, use original start/end times\n const range = dayRanges[0];\n if (!range) {\n return [];\n }\n const param: any = {\n Search: {\n channel,\n onlyStatus: 0,\n streamType,\n StartTime: range.start,\n EndTime: range.end,\n },\n };\n\n if (iLogicChannel > 0) {\n param.Search.iLogicChannel = iLogicChannel;\n }\n\n const response = await this.call<VodSearchResult>(\"Search\", param, 0);\n allResults.push(...(response as Array<VodSearchResponse>));\n } else {\n // Multiple days: query each day separately\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Range spans ${dayRanges.length} days, splitting into separate day queries`,\n );\n\n for (const range of dayRanges) {\n const param: any = {\n Search: {\n channel,\n onlyStatus: 0,\n streamType,\n StartTime: range.start,\n EndTime: range.end,\n },\n };\n\n if (iLogicChannel > 0) {\n param.Search.iLogicChannel = iLogicChannel;\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Querying day: ${range.start.year}-${range.start.mon}-${range.start.day} (${range.start.hour}:${range.start.min}:${range.start.sec} to ${range.end.hour}:${range.end.min}:${range.end.sec})`,\n );\n\n const response = await this.call<VodSearchResult>(\"Search\", param, 0);\n allResults.push(...(response as Array<VodSearchResponse>));\n }\n }\n\n const result = allResults;\n\n // Log raw API response for debugging\n if (result && result.length > 0) {\n const firstResult = result[0];\n if (\n firstResult &&\n firstResult.code === 0 &&\n firstResult.value?.SearchResult?.File\n ) {\n const files = firstResult.value.SearchResult.File;\n if (files.length > 0 && files[0]) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Raw API response (normal search): ${JSON.stringify({\n fileCount: files.length,\n sampleFile: {\n name: files[0].name,\n type: files[0].type,\n size: files[0].size,\n StartTime: files[0].StartTime,\n EndTime: files[0].EndTime,\n },\n })}`,\n );\n }\n }\n }\n\n // Collect all files from search results\n const allFiles: VodFile[] = [];\n for (const res of result) {\n if (res.code === 0) {\n const searchResult = res.value?.SearchResult;\n if (searchResult?.File) {\n allFiles.push(...searchResult.File);\n }\n }\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Collected ${allFiles.length} raw VodFiles from API search results`,\n );\n if (allFiles.length > 0) {\n // Log first few files as sample\n const sampleSize = Math.min(3, allFiles.length);\n for (let i = 0; i < sampleSize; i++) {\n const file = allFiles[i];\n if (file) {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Sample file ${i + 1}/${allFiles.length}: ${JSON.stringify({\n name: file.name,\n type: file.type,\n size: file.size,\n StartTime: file.StartTime,\n EndTime: file.EndTime,\n })}`,\n );\n }\n }\n }\n\n // Enrich each file\n const enriched: RecordingFile[] = [];\n const fetchStreamUrls = params.fetchStreamUrls === true;\n const streamUrlType = params.streamUrlType ?? \"FLV\";\n\n for (const vodFile of allFiles) {\n let streamUrl: string | undefined;\n\n // Optionally fetch streaming URL\n if (fetchStreamUrls) {\n try {\n const url = await this.getVodUrl(vodFile, channel, {\n requestType: streamUrlType,\n videoStreamType: streamType,\n prepare: true,\n });\n streamUrl = url;\n } catch (e) {\n // Silently ignore - not all recordings may have streaming URLs available\n }\n }\n\n enriched.push(this.enrichVodFile(vodFile, channel, streamUrl));\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclips\",\n `Returning ${enriched.length} enriched recording files`,\n );\n\n // Cache enriched results\n if (!bypassCache) {\n const cacheKey = this.getNvrRecordingsCacheKey(\n channel,\n startTime,\n endTime,\n streamType,\n iLogicChannel,\n autoSearchByDay,\n );\n this.recordingsCache.set(cacheKey, {\n data: enriched,\n expiresAt: Date.now() + this.recordingsCacheTtlMs,\n });\n }\n\n return enriched;\n }\n\n /**\n * Get a JPEG thumbnail from a VOD recording using ffmpeg.\n *\n * This method fetches the VOD URL (with Download type for better ffmpeg compatibility)\n * and extracts the first valid frame using ffmpeg.\n *\n * @param params - Parameters for thumbnail extraction\n * @returns JPEG buffer\n *\n * @example\n * ```typescript\n * const thumbnail = await cgiApi.getVideoclipThumbnailJpeg({\n * channel: 0,\n * filename: \"/mnt/sda/...\",\n * ffmpegPath: \"/usr/bin/ffmpeg\",\n * });\n * fs.writeFileSync(\"thumbnail.jpg\", thumbnail);\n * ```\n */\n async getVideoclipThumbnailJpeg(params: {\n /** Channel number (0-based) */\n channel: number;\n /** Recording filename or VodFile object from getVideoclips() */\n filename: string | VodFile;\n /** Path to ffmpeg executable */\n ffmpegPath: string;\n /** Timeout in milliseconds (default: 30000) */\n timeoutMs?: number;\n /** Seek position in seconds (default: 0, extracts first frame) */\n seekSeconds?: number;\n }): Promise<Buffer> {\n const {\n channel,\n filename,\n ffmpegPath,\n timeoutMs = 30000,\n seekSeconds = 0,\n } = params;\n\n // Get VOD URL with FLV type (includes authentication in URL)\n // FLV is more reliable than Download for ffmpeg since it embeds credentials\n const vodUrl = await this.getVodUrl(filename, channel, {\n requestType: \"FLV\",\n streamType: \"main\",\n prepare: true,\n seek: seekSeconds,\n });\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclipThumbnailJpeg\",\n `Extracting thumbnail from VOD URL (FLV): ${vodUrl.substring(0, 100)}... (seek=${seekSeconds}s)`,\n );\n\n // Use ffmpeg to extract first frame as JPEG\n const { spawn } = await import(\"child_process\");\n\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let stderr = \"\";\n let timedOut = false;\n\n // ffmpeg args optimized for HTTP FLV streams:\n // - analyzeduration/probesize: allow more time to find stream info\n // - fflags: handle streams with gaps/errors gracefully\n // - rw_timeout: prevent hanging on slow/broken connections\n const ffmpeg = spawn(ffmpegPath, [\n \"-y\",\n \"-analyzeduration\",\n \"10000000\",\n \"-probesize\",\n \"10000000\",\n \"-fflags\",\n \"+genpts+discardcorrupt+igndts\",\n \"-rw_timeout\",\n String(timeoutMs * 1000), // microseconds\n \"-i\",\n vodUrl,\n \"-vframes\",\n \"1\",\n \"-q:v\",\n \"2\",\n \"-f\",\n \"image2pipe\",\n \"-vcodec\",\n \"mjpeg\",\n \"pipe:1\",\n ]);\n\n const timer = setTimeout(() => {\n timedOut = true;\n ffmpeg.kill(\"SIGKILL\");\n reject(new Error(`ffmpeg timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n ffmpeg.stdout.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n ffmpeg.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n ffmpeg.on(\"close\", (code) => {\n clearTimeout(timer);\n if (timedOut) return;\n\n if (code === 0 && chunks.length > 0) {\n const jpeg = Buffer.concat(chunks);\n if (jpeg.length < 100) {\n reject(\n new Error(\n `ffmpeg produced too small output: ${jpeg.length} bytes`,\n ),\n );\n return;\n }\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"getVideoclipThumbnailJpeg\",\n `Successfully extracted thumbnail: ${jpeg.length} bytes`,\n );\n resolve(jpeg);\n } else {\n reject(\n new Error(`ffmpeg exited with code ${code}: ${stderr.slice(-500)}`),\n );\n }\n });\n\n ffmpeg.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(new Error(`ffmpeg spawn error: ${err.message}`));\n });\n });\n }\n\n /**\n * Prepare a VOD file for download on NVR/Hub.\n * This is required before downloading VOD files from NVR/Hub.\n * This command does NOT wake up battery cameras connected to the hub.\n *\n * @param channel - Channel number (0-based)\n * @param startTime - Start time for the recording\n * @param endTime - End time for the recording\n * @param streamType - Stream type: \"main\" (default), \"sub\", etc.\n * @param options - Optional parameters\n * @returns Prepared filename for download\n */\n async prepareNvrVodDownload(\n channel: number,\n startTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n endTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n streamType: string = \"main\",\n options?: {\n /** For multifocal cameras: logical channel (0 or 1) */\n iLogicChannel?: number;\n },\n ): Promise<string> {\n if (this.nvrDownloadPrepareSupport === \"unsupported\") {\n throw new Error(\"NvrDownload is not supported on this device\");\n }\n\n const iLogicChannel = options?.iLogicChannel ?? 0;\n\n // Ensure time format matches exactly (year, mon, day, hour, min, sec)\n const param = {\n NvrDownload: {\n channel,\n iLogicChannel,\n streamType,\n StartTime: {\n year: startTime.year,\n mon: startTime.mon,\n day: startTime.day,\n hour: startTime.hour,\n min: startTime.min,\n sec: startTime.sec,\n },\n EndTime: {\n year: endTime.year,\n mon: endTime.mon,\n day: endTime.day,\n hour: endTime.hour,\n min: endTime.min,\n sec: endTime.sec,\n },\n },\n };\n\n const body = [\n {\n cmd: \"NvrDownload\",\n action: 1,\n param: param,\n },\n ];\n\n // Log the request for debugging\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"prepareNvrVodDownload\",\n `Request body: ${JSON.stringify(body)}`,\n );\n\n try {\n const response = await this.callMany<{\n fileList: Array<{ fileName: string; fileSize: number }>;\n }>(body);\n\n // Log the response for debugging\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"prepareNvrVodDownload\",\n `Response: ${JSON.stringify(response)}`,\n );\n\n const first = response[0];\n if (!first || first.code !== 0 || !first.value?.fileList) {\n const rspCode = (first as any)?.error?.rspCode;\n const detail = String(\n (first as any)?.error?.detail ?? \"\",\n ).toLowerCase();\n // Some firmwares report rspCode=-17 for multiple causes (including invalid params).\n // Only mark as unsupported when the device explicitly says it's not supported.\n if (\n rspCode === -17 &&\n (detail.includes(\"not support\") ||\n detail.includes(\"unsupported\") ||\n detail.includes(\"not supported\"))\n ) {\n this.nvrDownloadPrepareSupport = \"unsupported\";\n }\n throw new Error(`NvrDownload failed: ${JSON.stringify(response)}`);\n }\n\n this.nvrDownloadPrepareSupport = \"supported\";\n\n // Return the filename of the largest file\n let maxFilesize = 0;\n let filename = \"\";\n for (const file of first.value.fileList) {\n const filesize = Number(file.fileSize);\n if (filesize > maxFilesize) {\n maxFilesize = filesize;\n filename = file.fileName;\n }\n }\n\n if (!filename) {\n throw new Error(`NvrDownload: no files found in response`);\n }\n\n return filename;\n } catch (error) {\n // Log error for debugging\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"prepareNvrVodDownload\",\n `Error: ${error instanceof Error ? error.message : String(error)}`,\n );\n throw error;\n }\n }\n\n /**\n * Get URL for VOD playback, download, or streaming.\n *\n * @param filenameOrVodFile - Filename string or VodFile object from getVideoclips\n * @param channel - Channel number (0-based)\n * @param options - Optional parameters\n * @returns URL string\n */\n async getVodUrl(\n filenameOrVodFile: string | VodFile,\n channel: number,\n options?: GetVodUrlParams,\n ): Promise<string> {\n await this.login();\n const requestType = options?.requestType ?? \"Playback\";\n const streamType =\n options?.streamType ?? options?.videoStreamType ?? \"main\";\n const videoStreamType = options?.videoStreamType ?? streamType;\n const seek = options?.seek ?? 0;\n\n // Extract filename from VodFile or use string directly\n const filename =\n typeof filenameOrVodFile === \"string\"\n ? filenameOrVodFile\n : filenameOrVodFile.name;\n\n // Get base URL from client\n const clientAny = this.client as any;\n const scheme = clientAny.useHttps ? \"https\" : \"http\";\n const port = clientAny.port ?? (clientAny.useHttps ? 443 : 80);\n const host = clientAny.host;\n const rtmpPort = 1935;\n\n // Encode filename: replace spaces with %20, but keep '/' as-is\n const encodedFilename = filename.replaceAll(\" \", \"%20\");\n\n // Map stream type to numeric value for FLV/RTMP\n let streamTypeNum = 0; // main\n if (videoStreamType === \"sub\") {\n streamTypeNum = 1;\n }\n\n let url: string;\n\n if (requestType === \"FLV\") {\n // FLV streaming URL - uses username/password authentication\n const username = encodeURIComponent(clientAny.username ?? \"\");\n const password = encodeURIComponent(clientAny.password ?? \"\");\n url = `${scheme}://${host}:${port}/flv?port=${rtmpPort}&app=bcs&stream=playback.bcs&channel=${channel}&type=${streamTypeNum}&start=${encodedFilename}&seek=${seek}&user=${username}&password=${password}`;\n } else if (requestType === \"RTMP\") {\n // RTMP streaming URL - uses username/password authentication\n const username = encodeURIComponent(clientAny.username ?? \"\");\n const password = encodeURIComponent(clientAny.password ?? \"\");\n url = `rtmp://${host}:${rtmpPort}/vod/${encodedFilename}?channel=${channel}&stream=${streamTypeNum}&user=${username}&password=${password}`;\n } else {\n // Playback or Download - use token authentication\n const token = this.client.getToken();\n if (!token) {\n throw new Error(\"Not logged in. Call login() first.\");\n }\n const cmd = requestType === \"NVR_DOWNLOAD\" ? \"Download\" : requestType;\n\n // Extract time_start from filename\n let startTimeParam = \"\";\n const timeMatch = filename.match(/Rec\\w{3}(?:_|_DST)?(\\d{8})_(\\d{6})_/);\n if (timeMatch) {\n startTimeParam = `&start=${timeMatch[1]}${timeMatch[2]}`;\n }\n\n const outputFilename = `playback_${Date.now()}.mp4`;\n url = `${scheme}://${host}:${port}/cgi-bin/api.cgi?cmd=${cmd}&source=${encodedFilename}&output=${outputFilename}${startTimeParam}&token=${encodeURIComponent(token)}`;\n }\n\n return url;\n }\n\n /**\n * Download a VOD file.\n * For NVR/Hub, use prepareNvrVodDownload first to get the filename.\n *\n * @param filename - Filename from getVideoclips or prepareNvrVodDownload\n * @param options - Optional download parameters\n * @returns Buffer containing the video file\n */\n async downloadVod(\n filename: string,\n options?: {\n /** Output filename */\n output?: string;\n /** Start time string */\n start?: string;\n },\n ): Promise<Buffer> {\n return await this.client.downloadVod(\n filename,\n options?.output,\n options?.start,\n );\n }\n\n /**\n * Parse recordType string to extract detection flags.\n */\n private parseRecordTypeFlags(recordType?: string): {\n hasPerson: boolean;\n hasVehicle: boolean;\n hasAnimal: boolean;\n hasFace: boolean;\n hasMotion: boolean;\n hasSchedule: boolean;\n hasDoorbell: boolean;\n hasPackage: boolean;\n hasRf: boolean;\n hasOther: boolean;\n } {\n const flags = {\n hasPerson: false,\n hasVehicle: false,\n hasAnimal: false,\n hasFace: false,\n hasMotion: false,\n hasSchedule: false,\n hasDoorbell: false,\n hasPackage: false,\n hasRf: false,\n hasOther: false,\n };\n\n if (!recordType) return flags;\n\n const types = recordType\n .toLowerCase()\n .split(/[,\\s]+/)\n .map((s) => s.trim())\n .filter(Boolean);\n\n for (const t of types) {\n if (t === \"people\" || t === \"person\") flags.hasPerson = true;\n else if (t === \"vehicle\" || t === \"car\") flags.hasVehicle = true;\n else if (t === \"dog_cat\" || t === \"animal\" || t === \"pet\")\n flags.hasAnimal = true;\n else if (t === \"face\") flags.hasFace = true;\n else if (t === \"md\" || t === \"motion\") flags.hasMotion = true;\n else if (t === \"sched\" || t === \"schedule\" || t === \"timer\")\n flags.hasSchedule = true;\n else if (t === \"visitor\" || t === \"doorbell\") flags.hasDoorbell = true;\n else if (t === \"package\") flags.hasPackage = true;\n else if (t === \"rf\" || t === \"io\" || t === \"pir\") flags.hasRf = true;\n else if (t === \"other\" || t === \"manual\") flags.hasOther = true;\n }\n\n return flags;\n }\n\n /**\n * Generate day-by-day ranges from startTime to endTime.\n * Each range covers exactly one day (00:00:00 to 23:59:59), except the first and last day\n * which use the original start/end times.\n *\n * @param startTime - Start time in Reolink format\n * @param endTime - End time in Reolink format\n * @returns Array of day ranges, each with \\{start, end\\} in Reolink time format\n */\n private generateDayRanges(\n startTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n endTime: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n },\n ): Array<{\n start: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n end: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n }> {\n const ranges: Array<{\n start: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n end: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n };\n }> = [];\n\n // Convert to Date objects to calculate day differences\n const startDate = new Date(\n startTime.year,\n startTime.mon - 1,\n startTime.day,\n startTime.hour,\n startTime.min,\n startTime.sec,\n );\n const endDate = new Date(\n endTime.year,\n endTime.mon - 1,\n endTime.day,\n endTime.hour,\n endTime.min,\n endTime.sec,\n );\n\n if (endDate < startDate) {\n // Invalid range, return empty\n return [];\n }\n\n // Check if same day\n const startDayKey = `${startTime.year}-${startTime.mon}-${startTime.day}`;\n const endDayKey = `${endTime.year}-${endTime.mon}-${endTime.day}`;\n\n if (startDayKey === endDayKey) {\n // Same day: single range with original times\n return [{ start: startTime, end: endTime }];\n }\n\n // Multiple days: generate ranges for each day\n let currentDate = new Date(startDate);\n\n while (currentDate <= endDate) {\n const currentYear = currentDate.getFullYear();\n const currentMonth = currentDate.getMonth() + 1;\n const currentDay = currentDate.getDate();\n const currentDayKey = `${currentYear}-${currentMonth}-${currentDay}`;\n\n if (currentDayKey === startDayKey) {\n // First day: use original start time, end at 23:59:59\n ranges.push({\n start: startTime,\n end: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 23,\n min: 59,\n sec: 59,\n },\n });\n } else if (currentDayKey === endDayKey) {\n // Last day: start at 00:00:00, use original end time\n ranges.push({\n start: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 0,\n min: 0,\n sec: 0,\n },\n end: endTime,\n });\n } else {\n // Middle day: full day (00:00:00 to 23:59:59)\n ranges.push({\n start: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 0,\n min: 0,\n sec: 0,\n },\n end: {\n year: currentYear,\n mon: currentMonth,\n day: currentDay,\n hour: 23,\n min: 59,\n sec: 59,\n },\n });\n }\n\n // Move to next day\n currentDate.setDate(currentDate.getDate() + 1);\n currentDate.setHours(0, 0, 0, 0);\n }\n\n return ranges;\n }\n\n /**\n * Convert Reolink time object to Date.\n */\n private reolinkTimeToDate(time: {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n }): Date {\n return new Date(\n time.year,\n time.mon - 1,\n time.day,\n time.hour,\n time.min,\n time.sec,\n );\n }\n\n /**\n * Convert Date to Reolink time object.\n * IMPORTANT: Uses LOCAL TIME values because the Reolink API interprets time values as local time\n * (matching the timezone of the camera/NVR). This ensures that when we pass time values to the API,\n * they match the values stored in filenames (e.g., \"20260106_072650\" means 6 January 2026, 07:26:50 local time).\n *\n * To ensure correct extraction of local time values, we create a new Date object from the local time\n * components, similar to how it's done in test files: `new Date().setHours(0, 0, 0, 0)`.\n * This normalizes the Date to represent the exact local time moment, avoiding any UTC conversion issues.\n */\n private dateToReolinkTime(date: Date): {\n year: number;\n mon: number;\n day: number;\n hour: number;\n min: number;\n sec: number;\n } {\n // Extract local time components first (these are what we want to pass to the API)\n const year = date.getFullYear();\n const month = date.getMonth();\n const day = date.getDate();\n const hour = date.getHours();\n const minute = date.getMinutes();\n const second = date.getSeconds();\n\n // Create a new Date object from these local time values to normalize it\n // This ensures we're working with a Date that represents the exact local time moment\n const normalizedDate = new Date(year, month, day, hour, minute, second);\n\n // Extract again from normalized date to be absolutely sure we have local time values\n // (This should be the same, but ensures consistency)\n return {\n year: normalizedDate.getFullYear(),\n mon: normalizedDate.getMonth() + 1,\n day: normalizedDate.getDate(),\n hour: normalizedDate.getHours(),\n min: normalizedDate.getMinutes(),\n sec: normalizedDate.getSeconds(),\n };\n }\n\n /**\n * Convert a VodFile to RecordingFile with parsed metadata.\n */\n private enrichVodFile(\n vodFile: VodFile,\n channel: number,\n streamUrl?: string,\n ): RecordingFile {\n // Log raw VodFile data from API (log entire object to see if there are hidden fields)\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `RAW VodFile from API: ${JSON.stringify({\n name: vodFile.name,\n type: vodFile.type,\n size: vodFile.size,\n StartTime: vodFile.StartTime,\n EndTime: vodFile.EndTime,\n PlaybackTime: vodFile.PlaybackTime,\n fullVodFile: JSON.parse(JSON.stringify(vodFile)),\n })}`,\n );\n\n // Parse filename\n const parsed = parseRecordingFileName(vodFile.name);\n\n // Extract hex value from filename for debugging\n const filenameParts = vodFile.name.split(\"_\");\n const hexValueFromFilename =\n filenameParts.length >= 8\n ? filenameParts[filenameParts.length - 2]\n : \"unknown\";\n // For version 4, the last part (hash) might contain detection info\n const hashPart =\n filenameParts.length >= 9\n ? filenameParts[filenameParts.length - 1]?.replace(\".mp4\", \"\")\n : null;\n\n // Debug: analyze hex value bit by bit\n let hexAnalysis: any = null;\n if (\n hexValueFromFilename &&\n hexValueFromFilename !== \"unknown\" &&\n /^[0-9a-fA-F]+$/.test(hexValueFromFilename)\n ) {\n const bitLen = hexValueFromFilename.length * 4;\n const hexInt = BigInt(`0x${hexValueFromFilename}`);\n const bin = hexInt.toString(2).padStart(bitLen, \"0\");\n const revBin = bin.split(\"\").reverse().join(\"\");\n\n // For Hub v4, analyze all bit ranges to find where detection flags might be\n const bitRanges: Record<string, string> = {};\n for (let start = 0; start < Math.min(bitLen, 64); start += 8) {\n const end = Math.min(start + 8, bitLen);\n bitRanges[`bits${start}to${end}`] = revBin.slice(start, end);\n }\n\n hexAnalysis = {\n hexValue: hexValueFromFilename,\n hexInt: hexInt.toString(),\n binary: bin,\n reversedBinary: revBin,\n bitLength: bitLen,\n // Show bits 17-27 which should contain detection flags (for older versions)\n bits17to27: revBin.slice(17, 28),\n // Show all 8-bit ranges for comprehensive analysis\n allBitRanges: bitRanges,\n // Show specific ranges that might contain detection flags for v4\n bits0to16: revBin.slice(0, 17),\n bits17to32: revBin.slice(17, 33),\n bits33to48: revBin.slice(33, 49),\n bits49to56: revBin.slice(49, 56),\n };\n }\n\n // Debug: analyze hash part (might contain detection info for version 4)\n let hashAnalysis: any = null;\n if (hashPart && /^[0-9a-fA-F]+$/i.test(hashPart)) {\n const hashBitLen = hashPart.length * 4;\n const hashHexInt = BigInt(`0x${hashPart}`);\n const hashBin = hashHexInt.toString(2).padStart(hashBitLen, \"0\");\n const hashRevBin = hashBin.split(\"\").reverse().join(\"\");\n hashAnalysis = {\n hashValue: hashPart,\n hashHexInt: hashHexInt.toString(),\n hashBinary: hashBin,\n hashReversedBinary: hashRevBin,\n hashBitLength: hashBitLen,\n // Show first 16 bits which might contain detection flags\n hashBits0to15: hashRevBin.slice(0, 16),\n };\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Parsed filename: ${JSON.stringify({\n fileName: vodFile.name,\n filenameParts: filenameParts,\n hexValueFromFilename: hexValueFromFilename,\n hashPart: hashPart,\n hexAnalysis: hexAnalysis,\n hashAnalysis: hashAnalysis,\n parsed: parsed\n ? {\n start: parsed.start?.toISOString(),\n end: parsed.end?.toISOString(),\n durationMs: parsed.durationMs,\n flags: parsed.flags,\n rawFlags: parsed.rawFlags,\n streamHint: parsed.streamHint,\n devType: parsed.devType,\n version: parsed.version,\n }\n : null,\n })}`,\n );\n\n // Get times from various sources\n const startTime =\n parsed?.start ?? this.reolinkTimeToDate(vodFile.StartTime);\n const endTime = parsed?.end ?? this.reolinkTimeToDate(vodFile.EndTime);\n\n const startTimeMs = startTime.getTime();\n const endTimeMs = endTime.getTime();\n\n // Calculate duration\n let durationMs = parsed?.durationMs ?? 0;\n if (durationMs === 0 && endTimeMs > startTimeMs) {\n durationMs = endTimeMs - startTimeMs;\n }\n\n // Get flags from hex decoding (from parsed filename)\n const hexFlags = parsed?.flags;\n\n // For Hub v4, check if hex value is constant (suggests detection info might not be in filename)\n if (parsed?.devType === \"hub\" && parsed?.version === 4) {\n if (hexValueFromFilename && hexValueFromFilename !== \"unknown\") {\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `WARNING: Hub v4 hex value \"${hexValueFromFilename}\" appears to be constant across files. Detection flags are likely NOT encoded in filename for this version. Detection information may need to be retrieved via a separate API call or may not be available.`,\n );\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `NOTE: For Hub v4, the API response only provides 'type' field which is \"${vodFile.type}\". No detection-specific fields are available in the Search API response.`,\n );\n }\n }\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Hex flags from filename: ${JSON.stringify(hexFlags)}`,\n );\n\n // Get flags from recordType string (if available in VodFile)\n // Note: VodFile from HTTP API doesn't have recordType, but we can try to infer from triggers\n const typeFlags = this.parseRecordTypeFlags(vodFile.type);\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Type flags from recordType \"${vodFile.type}\": ${JSON.stringify(typeFlags)}`,\n );\n\n // Merge flags: OR them together\n const hasPerson = (hexFlags?.aiPerson ?? false) || typeFlags.hasPerson;\n const hasVehicle = (hexFlags?.aiVehicle ?? false) || typeFlags.hasVehicle;\n const hasAnimal = (hexFlags?.aiAnimal ?? false) || typeFlags.hasAnimal;\n const hasFace = (hexFlags?.aiFace ?? false) || typeFlags.hasFace;\n const hasMotion = (hexFlags?.motion ?? false) || typeFlags.hasMotion;\n const hasSchedule = (hexFlags?.schedule ?? false) || typeFlags.hasSchedule;\n const hasDoorbell = (hexFlags?.doorbell ?? false) || typeFlags.hasDoorbell;\n const hasPackage = (hexFlags?.package ?? false) || typeFlags.hasPackage;\n const hasRf = (hexFlags?.rf ?? false) || typeFlags.hasRf;\n const hasOther = (hexFlags?.aiOther ?? false) || typeFlags.hasOther;\n\n recordingsTraceLog(\n this.debugConfig,\n this.logger,\n \"enrichVodFile\",\n `Final merged flags: ${JSON.stringify({\n hasPerson,\n hasVehicle,\n hasAnimal,\n hasFace,\n hasMotion,\n hasSchedule,\n hasDoorbell,\n hasPackage,\n hasRf,\n hasOther,\n })}`,\n );\n\n // Build detectionClasses array from merged flags\n const detectionClasses: RecordingDetectionClass[] = [];\n if (hasPerson) detectionClasses.push(\"person\");\n if (hasVehicle) detectionClasses.push(\"vehicle\");\n if (hasAnimal) detectionClasses.push(\"animal\");\n if (hasFace) detectionClasses.push(\"face\");\n if (hasMotion) detectionClasses.push(\"motion\");\n if (hasSchedule) detectionClasses.push(\"schedule\");\n if (hasDoorbell) detectionClasses.push(\"doorbell\");\n if (hasPackage) detectionClasses.push(\"package\");\n if (hasRf) detectionClasses.push(\"rf\");\n if (hasOther) detectionClasses.push(\"other\");\n\n // Default to motion if no detection classes found\n if (detectionClasses.length === 0) {\n detectionClasses.push(\"motion\");\n }\n\n // Create RecordingFile with all available metadata\n // Note: vodFile.size might come as a string from JSON, ensure it's a number\n const sizeBytes =\n typeof vodFile.size === \"string\"\n ? parseInt(vodFile.size, 10)\n : vodFile.size;\n\n const result: RecordingFile = {\n fileName: vodFile.name,\n id: vodFile.name,\n startTime,\n endTime,\n recordType: vodFile.type,\n detectionClasses,\n };\n\n // Only add sizeBytes if it's a valid number (due to exactOptionalPropertyTypes)\n if (Number.isFinite(sizeBytes)) {\n result.sizeBytes = sizeBytes;\n }\n\n if (parsed) {\n result.parsedFileName = parsed;\n }\n\n return result;\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport yazl from \"yazl\";\n\nasync function walkDir(rootDir: string, currentDir: string): Promise<Array<{ absPath: string; relPath: string; isDir: boolean }>> {\n const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });\n const out: Array<{ absPath: string; relPath: string; isDir: boolean }> = [];\n\n for (const ent of entries) {\n const absPath = path.join(currentDir, ent.name);\n const relPath = path.relative(rootDir, absPath).replace(/\\\\/g, \"/\");\n\n if (ent.isDirectory()) {\n out.push({ absPath, relPath, isDir: true });\n out.push(...(await walkDir(rootDir, absPath)));\n continue;\n }\n\n if (ent.isFile()) {\n out.push({ absPath, relPath, isDir: false });\n continue;\n }\n\n // Ignore symlinks/sockets/etc for diagnostics bundles.\n }\n\n return out;\n}\n\nexport async function zipDirectory(params: { sourceDir: string; zipPath: string }): Promise<void> {\n const { sourceDir, zipPath } = params;\n\n await fs.promises.mkdir(path.dirname(zipPath), { recursive: true });\n\n const zipFile = new yazl.ZipFile();\n\n const entries = await walkDir(sourceDir, sourceDir);\n const dirs = entries.filter((e) => e.isDir);\n const files = entries.filter((e) => !e.isDir);\n\n // Add directories first (best-effort; zip doesn't strictly require it).\n for (const d of dirs) {\n // yazl expects directories with trailing slash\n const name = d.relPath.endsWith(\"/\") ? d.relPath : d.relPath + \"/\";\n zipFile.addEmptyDirectory(name);\n }\n\n for (const f of files) {\n zipFile.addFile(f.absPath, f.relPath);\n }\n\n const out = fs.createWriteStream(zipPath);\n\n await new Promise<void>((resolve, reject) => {\n out.on(\"error\", reject);\n zipFile.outputStream.on(\"error\", reject);\n out.on(\"close\", resolve);\n\n zipFile.outputStream.pipe(out);\n zipFile.end();\n });\n}\n","export type RtspStreamProfile = \"main\" | \"sub\" | \"ext\";\n\nexport function buildRtspPath(channel: number, stream: RtspStreamProfile): string {\n // Standard Reolink path:\n // - channel is 0-based in most APIs, but RTSP path is 1-based and 2-digit.\n const ch = String(channel + 1).padStart(2, \"0\");\n let suffix: string;\n if (stream === \"main\") {\n suffix = \"main\";\n } else if (stream === \"sub\") {\n suffix = \"sub\";\n } else {\n // ext stream\n suffix = \"ext\";\n }\n return `/h264Preview_${ch}_${suffix}`;\n}\n\nexport function buildRtspUrl(params: {\n host: string;\n port?: number;\n username: string;\n password: string;\n channel: number;\n stream: RtspStreamProfile;\n}): string {\n const port = params.port ?? 554;\n const path = buildRtspPath(params.channel, params.stream);\n const user = encodeURIComponent(params.username);\n const pass = encodeURIComponent(params.password);\n return `rtsp://${user}:${pass}@${params.host}:${port}${path}`;\n}\n\n","export function xmlEscape(text: string | undefined | null): string {\n if (text === undefined || text === null || typeof text !== \"string\") {\n const error = new Error(\n `xmlEscape: expected string but got ${typeof text}: ${text}`,\n );\n throw error;\n }\n return text\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nexport function buildLoginXml(\n userNameHash: string,\n passwordHash: string,\n): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<LoginUser version=\"1.1\">\n<userName>${xmlEscape(userNameHash)}</userName>\n<password>${xmlEscape(passwordHash)}</password>\n<userVer>1</userVer>\n</LoginUser>\n<LoginNet version=\"1.1\">\n<type>LAN</type>\n<udpPort>0</udpPort>\n</LoginNet>\n</body>`;\n}\n\n/**\n * Build Logout XML for session termination.\n * PCAP-observed: cmdId=2 with encrypted XML body containing Logout element.\n */\nexport function buildLogoutXml(): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Logout version=\"1.1\">\n</Logout>\n</body>`;\n}\n\nexport function buildChannelExtensionXml(\n channelId: number | string | undefined | null,\n): string {\n if (channelId === undefined || channelId === null) {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Extension version=\"1.1\"></Extension>`;\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<Extension version=\"1.1\">\n<channelId>${channelId}</channelId>\n</Extension>`;\n}\n\nexport function buildBinaryExtensionXml(\n channelId: number | string | undefined | null,\n): string {\n if (channelId === undefined || channelId === null) {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Extension version=\"1.1\"><binaryData>1</binaryData></Extension>`;\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<Extension version=\"1.1\">\n<binaryData>1</binaryData>\n<channelId>${channelId}</channelId>\n</Extension>`;\n}\n\n/**\n * Build Preview XML for video stream request.\n * Uses Preview element with handle and stream_type.\n *\n * @param handle - Handle value: 0 for main, 256 for sub, 1024 for extern\n * @param streamType - Stream type name: \"mainStream\", \"subStream\", or \"externStream\"\n * @param channelId - Channel ID (optional, not used in working format)\n * @returns XML string for Preview element\n */\nexport function buildPreviewXml(\n handle: number,\n streamType: string | undefined | null,\n channelId?: number,\n): string {\n // Preview includes channelId + handle + streamType.\n if (!streamType || typeof streamType !== \"string\") {\n throw new Error(\n `buildPreviewXml: streamType is required (string) but got: ${typeof streamType} = ${streamType}`,\n );\n }\n const channelIdXml =\n channelId !== undefined ? `<channelId>${channelId}</channelId>\\n` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.0\">\n${channelIdXml}<handle>${handle}</handle>\n<streamType>${xmlEscape(streamType)}</streamType>\n</Preview>\n</body>`;\n}\n\n/**\n * Build Preview XML for video stream request (v1.1).\n *\n * This format is observed in Reolink client traffic to NVR/Hub where the Preview element uses\n * version=\"1.1\" and always includes channelId + handle + streamType.\n */\nexport function buildPreviewXmlV11(params: {\n channelId: number;\n handle: number;\n streamType: string;\n}): string {\n if (!Number.isFinite(params.channelId)) {\n throw new Error(\n `buildPreviewXmlV11: channelId must be finite, got: ${params.channelId}`,\n );\n }\n if (!Number.isFinite(params.handle)) {\n throw new Error(\n `buildPreviewXmlV11: handle must be finite, got: ${params.handle}`,\n );\n }\n if (!params.streamType || typeof params.streamType !== \"string\") {\n throw new Error(\n `buildPreviewXmlV11: streamType is required (string) but got: ${typeof params.streamType} = ${params.streamType}`,\n );\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.1\">\n<channelId>${params.channelId}</channelId>\n<handle>${params.handle}</handle>\n<streamType>${xmlEscape(params.streamType)}</streamType>\n</Preview>\n</body>`;\n}\n\n/**\n * Build Preview XML for video stream stop request.\n * Uses Preview element without stream_type.\n *\n * @param handle - Handle value: 0 for main, 256 for sub, 1024 for extern\n * @param channelId - Channel ID (optional, not used in working format)\n * @returns XML string for Preview element (stop)\n */\nexport function buildPreviewStopXml(\n handle: number,\n channelId?: number,\n): string {\n const channelIdXml =\n channelId !== undefined ? `<channelId>${channelId}</channelId>\\n` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.0\">\n${channelIdXml}<handle>${handle}</handle>\n</Preview>\n</body>`;\n}\n\n/**\n * Build Preview XML for video stream stop request (v1.1).\n *\n * This format is observed/needed on some Hub/NVR firmwares where the VIDEO start\n * request uses Preview version=\"1.1\" with explicit channelId and handle.\n */\nexport function buildPreviewStopXmlV11(params: {\n channelId: number;\n handle: number;\n}): string {\n if (!Number.isFinite(params.channelId)) {\n throw new Error(\n `buildPreviewStopXmlV11: channelId must be finite, got: ${params.channelId}`,\n );\n }\n if (!Number.isFinite(params.handle)) {\n throw new Error(\n `buildPreviewStopXmlV11: handle must be finite, got: ${params.handle}`,\n );\n }\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<Preview version=\"1.1\">\n<channelId>${params.channelId}</channelId>\n<handle>${params.handle}</handle>\n</Preview>\n</body>`;\n}\n\n/**\n * Very small helper for extracting simple `<tag>value</tag>` text from Baichuan XML.\n * This is intentionally dependency-free; it's enough for nonce/login flows.\n */\nexport function getXmlText(xml: string, tagName: string): string | undefined {\n const re = new RegExp(`<${tagName}>([^<]*)</${tagName}>`);\n const m = re.exec(xml);\n return m?.[1];\n}\n\n/**\n * Build PTZ Control XML for pan/tilt/zoom commands.\n *\n * @param channelId - Channel ID (1-based)\n * @param command - PTZ command: \"up\", \"down\", \"left\", \"right\", \"stop\"\n * @param speed - Movement speed (0.0 to 1.0, typically converted to 1-10)\n * @returns XML string for PtzControl element\n */\nexport function buildPtzControlXml(\n channelId: number,\n command: string,\n speed: number,\n): string {\n // PtzControl structure: channel_id, speed, command\n // Version is \"1.1\"\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<PtzControl version=\"1.1\">\n<channelId>${channelId}</channelId>\n<command>${xmlEscape(command)}</command>\n<speed>${speed}</speed>\n</PtzControl>\n</body>`;\n}\n\n/**\n * Build PTZ Preset XML for setting or moving to preset.\n *\n * @param channelId - Channel ID (1-based)\n * @param presetId - Preset ID (1-255)\n * @param command - \"setPos\" to save current position, \"toPos\" to move to preset\n * @param name - Preset name (optional, required for setPos)\n * @returns XML string for PtzPreset element\n */\nexport function buildPtzPresetXml(\n channelId: number,\n presetId: number,\n command: \"setPos\" | \"toPos\",\n name?: string,\n): string {\n return buildPtzPresetXmlV2(\n channelId,\n presetId,\n command,\n name === undefined ? undefined : { name },\n );\n}\n\nexport function buildPtzPresetXmlV2(\n channelId: number,\n presetId: number,\n command: \"setPos\" | \"toPos\" | \"delPos\",\n options?: {\n /** Preset name. For setPos many firmwares require it; empty string will emit an empty <name></name>. */\n name?: string;\n /** Best-effort enable/disable support. Some firmwares include this in the preset list response. */\n enable?: 0 | 1;\n },\n): string {\n let nameXml = \"\";\n const name = options?.name;\n if (command === \"setPos\" && name !== undefined) {\n nameXml = `<name>${xmlEscape(name)}</name>`;\n }\n\n let enableXml = \"\";\n if (options?.enable !== undefined) {\n enableXml = `<enable>${options.enable}</enable>`;\n }\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<PtzPreset version=\"1.1\">\n<channelId>${channelId}</channelId>\n<presetList>\n<preset>\n<id>${presetId}</id>\n<command>${command}</command>\n${nameXml}\n${enableXml}\n</preset>\n</presetList>\n</PtzPreset>\n</body>`;\n}\n\n/**\n * Build StartZoomFocus XML for zooming to an absolute position.\n *\n * cmd_id: 295 (MSG_ID_SET_ZOOM_FOCUS)\n *\n * @param channelId - Channel ID (0/1-based depending on camera; should match header/extension)\n * @param movePos - Absolute zoom position (typically 1000 == 1.0x)\n */\nexport function buildStartZoomFocusXml(\n channelId: number,\n movePos: number,\n): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<StartZoomFocus version=\"1.1\">\n<channelId>${channelId}</channelId>\n<command>zoomPos</command>\n<movePos>${Math.trunc(movePos)}</movePos>\n</StartZoomFocus>\n</body>`;\n}\n\n/**\n * Build Siren/Audio Alarm XML for manual control.\n *\n * @param channelId - Channel ID (1-based, optional for hub-level)\n * @param enable - Enable/disable siren (1 or 0)\n * @returns XML string for audioPlayInfo element\n */\nexport function buildSirenManualXml(\n channelId: number | undefined,\n enable: number,\n): string {\n const channelXml =\n channelId !== undefined ? `<channelId>${channelId}</channelId>` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<audioPlayInfo version=\"1.1\">\n${channelXml}\n<playMode>2</playMode>\n<playDuration>10</playDuration>\n<playTimes>1</playTimes>\n<onOff>${enable}</onOff>\n</audioPlayInfo>\n</body>`;\n}\n\n/**\n * Build Siren/Audio Alarm XML for times-based control.\n *\n * @param channelId - Channel ID (1-based, optional for hub-level)\n * @param times - Number of times to play\n * @returns XML string for audioPlayInfo element\n */\nexport function buildSirenTimesXml(\n channelId: number | undefined,\n times: number,\n): string {\n const channelXml =\n channelId !== undefined ? `<channelId>${channelId}</channelId>` : \"\";\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<audioPlayInfo version=\"1.1\">\n${channelXml}\n<playMode>0</playMode>\n<playDuration>10</playDuration>\n<playTimes>${times}</playTimes>\n<onOff>1</onOff>\n</audioPlayInfo>\n</body>`;\n}\n\n/**\n * Build FloodlightManual XML.\n * Used for cmd_id 288.\n *\n * Notes:\n * - channelId is 0-based (same as Baichuan channel id)\n * - status: 1 = on, 0 = off\n * - duration is in seconds\n */\nexport function buildFloodlightManualXml(\n channelId: number,\n status: number,\n durationSeconds = 180,\n): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<body>\n<FloodlightManual version=\"1.1\">\n<channelId>${channelId}</channelId>\n<status>${status}</status>\n<duration>${durationSeconds}</duration>\n</FloodlightManual>\n</body>`;\n}\n\n/**\n * Back-compat alias: historically this project called floodlight control \"WhiteLed\".\n * In practice, many cameras expect FloodlightManual payload for cmd 288.\n */\nexport function buildWhiteLedStateXml(\n channelId: number,\n state: number,\n): string {\n return buildFloodlightManualXml(channelId, state ? 1 : 0);\n}\n\n// ====================================================================\n// Read-modify-write helpers for Baichuan binary settings setters.\n//\n// Reolink firmwares are XML-strict — they reject payloads that drop\n// unmodified attributes or reorder elements. The setX methods on\n// `ReolinkBaichuanApi` follow reolink_aio's pattern: read the current\n// XML, mutate only the targeted tag(s) via regex, and write the same\n// document back. These helpers encapsulate the regex churn so each\n// setter stays a one-liner.\n// ====================================================================\n\nconst XML_HEADER = `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>`;\n\n/**\n * Prepend the XML declaration if the body doesn't already start with\n * one. Reolink rejects payloads without it on most setX commands.\n */\nexport function ensureXmlHeader(xml: string): string {\n const trimmed = xml.trimStart();\n if (trimmed.startsWith(\"<?xml\")) return xml;\n return `${XML_HEADER}\\n${xml}`;\n}\n\n/**\n * Replace the text content of `<tag>...</tag>` (first match) with the\n * stringified value. No-op when `value` is undefined — lets callers\n * pass partial patches without branching at every field.\n */\nexport function applyXmlTagPatch(\n xml: string,\n tag: string,\n value: string | number | boolean | undefined,\n): string {\n if (value === undefined) return xml;\n const v = typeof value === \"boolean\" ? (value ? 1 : 0) : value;\n const re = new RegExp(`<${tag}>[^<]*</${tag}>`);\n return xml.replace(re, `<${tag}>${v}</${tag}>`);\n}\n\n/**\n * Like {@link applyXmlTagPatch} but inserts the tag at the end of the block\n * when it is not already present. Required for fields the camera omits on\n * GET responses but expects on SET (e.g. `<encoderType>` on `setEnc`).\n */\nexport function upsertXmlTag(\n xml: string,\n tag: string,\n value: string | number | boolean | undefined,\n): string {\n if (value === undefined) return xml;\n const v = typeof value === \"boolean\" ? (value ? 1 : 0) : value;\n const re = new RegExp(`<${tag}>[^<]*</${tag}>`);\n if (re.test(xml)) {\n return xml.replace(re, `<${tag}>${v}</${tag}>`);\n }\n return `${xml}<${tag}>${v}</${tag}>`;\n}\n\n/**\n * Patch a child tag inside a named parent block. Used for nested\n * structures like `<DayNight><mode>...</mode></DayNight>` where the\n * same `<mode>` tag appears under multiple parents.\n */\nexport function patchNestedTag(\n xml: string,\n parent: string,\n child: string,\n value: string | number | boolean | undefined,\n): string {\n if (value === undefined) return xml;\n const v = typeof value === \"boolean\" ? (value ? 1 : 0) : value;\n // Match <parent>…<child>...</child>…</parent>, keep everything else.\n const re = new RegExp(\n `(<${parent}[^>]*>[\\\\s\\\\S]*?<${child}>)[^<]*(</${child}>[\\\\s\\\\S]*?</${parent}>)`,\n );\n return xml.replace(re, `$1${v}$2`);\n}\n\n/**\n * Patch fields inside a `<Compression>` stream block (`<mainStream>`,\n * `<subStream>`, or `<thirdStream>`). Used by `setEnc` — Reolink emits all\n * three blocks in the same document so a per-block scope is needed to avoid\n * clobbering the wrong stream.\n *\n * The mapping `videoEncType` ↔ codec uses the convention seen on the wire:\n * 0 = H.264\n * 1 = H.265\n * Both `<frame>` (camera-side preferred) and `<frameRate>` (older reolink_aio\n * naming) are patched defensively — no-op if only one is present.\n */\nexport function applyStreamPatch(\n xml: string,\n streamTag: \"mainStream\" | \"subStream\" | \"thirdStream\",\n patch:\n | {\n audio?: 0 | 1;\n width?: number;\n height?: number;\n bitRate?: number;\n frameRate?: number;\n videoEncType?: \"h264\" | \"h265\";\n encoderType?: \"vbr\" | \"cbr\";\n encoderProfile?: \"high\" | \"main\" | \"baseline\";\n gop?: number;\n }\n | undefined,\n): string {\n if (!patch) return xml;\n const re = new RegExp(\n `(<${streamTag}[^>]*>)([\\\\s\\\\S]*?)(</${streamTag}>)`,\n );\n return xml.replace(re, (_match, open: string, body: string, close: string) => {\n let next = body;\n if (patch.audio !== undefined) {\n next = applyXmlTagPatch(next, \"audio\", patch.audio);\n }\n if (patch.width !== undefined) {\n next = applyXmlTagPatch(next, \"width\", patch.width);\n }\n if (patch.height !== undefined) {\n next = applyXmlTagPatch(next, \"height\", patch.height);\n }\n if (patch.bitRate !== undefined) {\n next = applyXmlTagPatch(next, \"bitRate\", patch.bitRate);\n }\n if (patch.frameRate !== undefined) {\n next = applyXmlTagPatch(next, \"frameRate\", patch.frameRate);\n next = applyXmlTagPatch(next, \"frame\", patch.frameRate);\n }\n if (patch.videoEncType !== undefined) {\n const intVal = patch.videoEncType === \"h265\" ? 1 : 0;\n next = applyXmlTagPatch(next, \"videoEncType\", intVal);\n }\n if (patch.encoderType !== undefined) {\n next = upsertXmlTag(next, \"encoderType\", patch.encoderType);\n }\n if (patch.encoderProfile !== undefined) {\n next = upsertXmlTag(next, \"encoderProfile\", patch.encoderProfile);\n }\n if (patch.gop !== undefined) {\n // gop has nested <cur>/<max>/<min> — only <cur> is writable.\n const gopBlockRe = /(<gop[^>]*>)([\\s\\S]*?)(<\\/gop>)/;\n if (gopBlockRe.test(next)) {\n next = next.replace(\n gopBlockRe,\n (_m, gOpen: string, gBody: string, gClose: string) =>\n `${gOpen}${applyXmlTagPatch(gBody, \"cur\", patch.gop!)}${gClose}`,\n );\n } else {\n // Sub/third streams sometimes omit <gop>; inject a minimal block.\n next = `${next}<gop><cur>${patch.gop}</cur></gop>`;\n }\n }\n return `${open}${next}${close}`;\n });\n}\n\n/**\n * Normalize human-friendly day/night labels to the camera's expected\n * lowercase form. Mirrors reolink_aio's `SetIsp` post-processing\n * (`& → And`, capitalize first letter).\n */\nexport function normalizeDayNightMode(input: string): string {\n const stripped = String(input).replace(/&/g, \"And\");\n if (!stripped) return stripped;\n const first = stripped[0];\n if (first === undefined) return stripped;\n return first.toLowerCase() + stripped.slice(1);\n}\n\n/**\n * Normalize \"On\"/\"Off\" / \"open\"/\"close\" / boolean-ish inputs to the\n * `open`/`close` enum the camera expects on LED-control commands.\n */\nexport function normalizeOpenClose(input: string): string {\n const v = String(input).toLowerCase();\n if (v === \"on\" || v === \"open\" || v === \"1\" || v === \"true\") return \"open\";\n return \"close\";\n}\n\n/**\n * Build AbilityInfo extension XML for requesting device capabilities.\n * Requests all available tokens: \"system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image\"\n *\n * @param username - Username for the request\n * @returns XML string for Extension element with AbilityInfo request\n */\nexport function buildAbilityInfoExtensionXml(username: string): string {\n // Request all available ability tokens to get comprehensive ability information\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<Extension version=\"1.1\">\n<userName>${xmlEscape(username)}</userName>\n<token>system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image</token>\n</Extension>`;\n}\n"],"mappings":"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","names":["fs","path","start","end","durationMs","decoded","parsed","allResults","allFiles","enriched","fetchStreamUrls","streamUrlType","spawn","join","path","nals","splitAnnexBToNalPayloads","status","frameReceived","join","path","result","ok"]}
|