@camstack/addon-pipeline 0.1.0 → 0.1.1

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.
@@ -173,6 +173,8 @@ interface RecordingCoordinatorConfig {
173
173
  readonly storagePath: string;
174
174
  readonly globalFfmpegConfig: Partial<FfmpegConfig>;
175
175
  readonly detectedFfmpegConfig: Partial<FfmpegConfig>;
176
+ /** Global segment duration from system settings (recording.segmentDurationSeconds). */
177
+ readonly segmentDurationSec?: number;
176
178
  }
177
179
  declare class RecordingCoordinator$1 {
178
180
  private readonly db;
@@ -185,6 +187,7 @@ declare class RecordingCoordinator$1 {
185
187
  private readonly storagePath;
186
188
  private readonly globalFfmpegConfig;
187
189
  private readonly detectedFfmpegConfig;
190
+ private readonly segmentDurationSec;
188
191
  private readonly recordings;
189
192
  private policyTimer;
190
193
  private readonly retentionManager;
@@ -219,8 +222,6 @@ declare class PipelineAddon implements ICamstackAddon, IConfigurable {
219
222
  private sqliteDb;
220
223
  private recordingDeps;
221
224
  private currentRecordingConfig;
222
- private analysisPipeline;
223
- private analysisLogger;
224
225
  private persistenceFacade;
225
226
  setRecordingDependencies(deps: RecordingEngineDependencies): void;
226
227
  initialize(context: AddonContext): Promise<void>;
@@ -228,8 +229,6 @@ declare class PipelineAddon implements ICamstackAddon, IConfigurable {
228
229
  getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
229
230
  getCoordinator(): RecordingCoordinator;
230
231
  getRecordingDb(): RecordingDb;
231
- /** Whether the analysis pipeline package loaded successfully */
232
- isAnalysisAvailable(): boolean;
233
232
  getConfigSchema(): ConfigUISchema;
234
233
  getConfig(): Record<string, unknown>;
235
234
  onConfigChange(config: Record<string, unknown>): Promise<void>;
@@ -173,6 +173,8 @@ interface RecordingCoordinatorConfig {
173
173
  readonly storagePath: string;
174
174
  readonly globalFfmpegConfig: Partial<FfmpegConfig>;
175
175
  readonly detectedFfmpegConfig: Partial<FfmpegConfig>;
176
+ /** Global segment duration from system settings (recording.segmentDurationSeconds). */
177
+ readonly segmentDurationSec?: number;
176
178
  }
177
179
  declare class RecordingCoordinator$1 {
178
180
  private readonly db;
@@ -185,6 +187,7 @@ declare class RecordingCoordinator$1 {
185
187
  private readonly storagePath;
186
188
  private readonly globalFfmpegConfig;
187
189
  private readonly detectedFfmpegConfig;
190
+ private readonly segmentDurationSec;
188
191
  private readonly recordings;
189
192
  private policyTimer;
190
193
  private readonly retentionManager;
@@ -219,8 +222,6 @@ declare class PipelineAddon implements ICamstackAddon, IConfigurable {
219
222
  private sqliteDb;
220
223
  private recordingDeps;
221
224
  private currentRecordingConfig;
222
- private analysisPipeline;
223
- private analysisLogger;
224
225
  private persistenceFacade;
225
226
  setRecordingDependencies(deps: RecordingEngineDependencies): void;
226
227
  initialize(context: AddonContext): Promise<void>;
@@ -228,8 +229,6 @@ declare class PipelineAddon implements ICamstackAddon, IConfigurable {
228
229
  getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
229
230
  getCoordinator(): RecordingCoordinator;
230
231
  getRecordingDb(): RecordingDb;
231
- /** Whether the analysis pipeline package loaded successfully */
232
- isAnalysisAvailable(): boolean;
233
232
  getConfigSchema(): ConfigUISchema;
234
233
  getConfig(): Record<string, unknown>;
235
234
  onConfigChange(config: Record<string, unknown>): Promise<void>;
package/dist/addon.cjs CHANGED
@@ -36,6 +36,7 @@ __export(ffmpeg_config_exports, {
36
36
  buildFfmpegInputArgs: () => buildFfmpegInputArgs,
37
37
  buildFfmpegOutputArgs: () => buildFfmpegOutputArgs,
38
38
  detectPlatformDefaults: () => detectPlatformDefaults,
39
+ resolveFfmpegBinary: () => resolveFfmpegBinary,
39
40
  resolveFfmpegConfig: () => resolveFfmpegConfig
40
41
  });
41
42
  function detectPlatformDefaults(ffmpegPath = "ffmpeg") {
@@ -78,12 +79,19 @@ function buildFfmpegOutputArgs(config) {
78
79
  if (config.outputArgs?.length) args.push(...config.outputArgs);
79
80
  return args;
80
81
  }
81
- var import_node_child_process3, os;
82
+ async function resolveFfmpegBinary(configPath, dataDir, logger) {
83
+ if (configPath && configPath !== "ffmpeg") {
84
+ return configPath;
85
+ }
86
+ return (0, import_core.ensureFfmpeg)(dataDir, logger);
87
+ }
88
+ var import_node_child_process3, os, import_core;
82
89
  var init_ffmpeg_config = __esm({
83
90
  "src/recording/ffmpeg-config.ts"() {
84
91
  "use strict";
85
92
  import_node_child_process3 = require("child_process");
86
93
  os = __toESM(require("os"), 1);
94
+ import_core = require("@camstack/core");
87
95
  }
88
96
  });
89
97
 
@@ -1189,7 +1197,7 @@ var recording_coordinator_exports = {};
1189
1197
  __export(recording_coordinator_exports, {
1190
1198
  RecordingCoordinator: () => RecordingCoordinator
1191
1199
  });
1192
- var POLICY_EVAL_INTERVAL_MS, MOTION_FALLBACK_TIMEOUT_MS, RecordingCoordinator;
1200
+ var DEFAULT_SEGMENT_DURATION_SEC, POLICY_EVAL_INTERVAL_MS, MOTION_FALLBACK_TIMEOUT_MS, RecordingCoordinator;
1193
1201
  var init_recording_coordinator = __esm({
1194
1202
  "src/recording/recording-coordinator.ts"() {
1195
1203
  "use strict";
@@ -1199,6 +1207,7 @@ var init_recording_coordinator = __esm({
1199
1207
  init_retention_manager();
1200
1208
  init_playlist_generator();
1201
1209
  init_storage_estimator();
1210
+ DEFAULT_SEGMENT_DURATION_SEC = 4;
1202
1211
  POLICY_EVAL_INTERVAL_MS = 1e3;
1203
1212
  MOTION_FALLBACK_TIMEOUT_MS = 6e4;
1204
1213
  RecordingCoordinator = class _RecordingCoordinator {
@@ -1212,6 +1221,7 @@ var init_recording_coordinator = __esm({
1212
1221
  storagePath;
1213
1222
  globalFfmpegConfig;
1214
1223
  detectedFfmpegConfig;
1224
+ segmentDurationSec;
1215
1225
  recordings = /* @__PURE__ */ new Map();
1216
1226
  policyTimer = null;
1217
1227
  retentionManager;
@@ -1228,6 +1238,7 @@ var init_recording_coordinator = __esm({
1228
1238
  this.storagePath = config.storagePath;
1229
1239
  this.globalFfmpegConfig = config.globalFfmpegConfig;
1230
1240
  this.detectedFfmpegConfig = config.detectedFfmpegConfig;
1241
+ this.segmentDurationSec = config.segmentDurationSec ?? DEFAULT_SEGMENT_DURATION_SEC;
1231
1242
  this.retentionManager = new RetentionManager(
1232
1243
  this.db,
1233
1244
  this.logger.child("retention"),
@@ -1313,7 +1324,7 @@ var init_recording_coordinator = __esm({
1313
1324
  const writerConfig = {
1314
1325
  deviceId,
1315
1326
  streamId: sp.streamId,
1316
- segmentDurationSec: 4,
1327
+ segmentDurationSec: this.segmentDurationSec,
1317
1328
  storagePath: this.storagePath,
1318
1329
  storageName,
1319
1330
  subDirectory,
@@ -3166,9 +3177,7 @@ var PipelineAddon = class {
3166
3177
  capabilities: [
3167
3178
  { name: "stream-broker", mode: "singleton" },
3168
3179
  { name: "recording-engine", mode: "singleton" },
3169
- { name: "analysis-pipeline", mode: "singleton" },
3170
- { name: "analysis-data-persistence", mode: "singleton" },
3171
- { name: "webrtc", mode: "collection" }
3180
+ { name: "analysis-data-persistence", mode: "singleton" }
3172
3181
  ]
3173
3182
  };
3174
3183
  // Stream broker
@@ -3181,11 +3190,10 @@ var PipelineAddon = class {
3181
3190
  currentRecordingConfig = {
3182
3191
  ffmpegPath: "ffmpeg",
3183
3192
  hwaccel: void 0,
3184
- threads: void 0
3193
+ threads: void 0,
3194
+ segmentDurationSeconds: 4,
3195
+ defaultRetentionDays: 30
3185
3196
  };
3186
- // Analysis pipeline
3187
- analysisPipeline = null;
3188
- analysisLogger = null;
3189
3197
  // Analysis persistence
3190
3198
  persistenceFacade = null;
3191
3199
  setRecordingDependencies(deps) {
@@ -3215,10 +3223,14 @@ var PipelineAddon = class {
3215
3223
  hwaccel: context.addonConfig.hwaccel ?? this.currentRecordingConfig.hwaccel,
3216
3224
  threads: context.addonConfig.threads ?? this.currentRecordingConfig.threads
3217
3225
  };
3226
+ const segmentDurationSeconds = context.addonConfig.segmentDurationSeconds ?? this.currentRecordingConfig.segmentDurationSeconds;
3227
+ const defaultRetentionDays = context.addonConfig.defaultRetentionDays ?? this.currentRecordingConfig.defaultRetentionDays;
3218
3228
  this.currentRecordingConfig = {
3219
3229
  ffmpegPath,
3220
3230
  hwaccel: globalFfmpegConfig.hwaccel,
3221
- threads: globalFfmpegConfig.threads
3231
+ threads: globalFfmpegConfig.threads,
3232
+ segmentDurationSeconds,
3233
+ defaultRetentionDays
3222
3234
  };
3223
3235
  const fileStorage = context.storage.files;
3224
3236
  if (!fileStorage) {
@@ -3234,7 +3246,8 @@ var PipelineAddon = class {
3234
3246
  fileStorage,
3235
3247
  storagePath,
3236
3248
  globalFfmpegConfig,
3237
- detectedFfmpegConfig
3249
+ detectedFfmpegConfig,
3250
+ segmentDurationSec: segmentDurationSeconds
3238
3251
  });
3239
3252
  await this.coordinator.start();
3240
3253
  context.logger.info("Recording Engine initialized");
@@ -3243,16 +3256,6 @@ var PipelineAddon = class {
3243
3256
  context.logger.warn(`Recording Engine failed to initialize: ${msg}`);
3244
3257
  }
3245
3258
  }
3246
- this.analysisLogger = context.logger;
3247
- try {
3248
- const mod = await import("@camstack/lib-pipeline-analysis");
3249
- const instance = new mod.AnalysisPipeline();
3250
- this.analysisPipeline = instance;
3251
- this.analysisLogger.info("Analysis pipeline loaded successfully");
3252
- } catch (error) {
3253
- const msg = error instanceof Error ? error.message : String(error);
3254
- this.analysisLogger.warn(`Analysis pipeline not available: ${msg} -- analysis features disabled`);
3255
- }
3256
3259
  const eventPersistence = new EventPersistenceService({
3257
3260
  getStorageLocation: () => context.storage,
3258
3261
  subscribe: (filter, handler) => context.eventBus.subscribe(filter, handler),
@@ -3304,7 +3307,6 @@ var PipelineAddon = class {
3304
3307
  this.sqliteDb = null;
3305
3308
  }
3306
3309
  this.recordingDb = null;
3307
- this.analysisPipeline = null;
3308
3310
  if (this.persistenceFacade) {
3309
3311
  this.persistenceFacade.eventPersistence.stop();
3310
3312
  this.persistenceFacade.retention.stop();
@@ -3317,13 +3319,8 @@ var PipelineAddon = class {
3317
3319
  return this.brokerManager;
3318
3320
  case "recording-engine":
3319
3321
  return this.coordinator;
3320
- case "analysis-pipeline":
3321
- return this.analysisPipeline;
3322
3322
  case "analysis-data-persistence":
3323
3323
  return this.persistenceFacade;
3324
- case "webrtc":
3325
- return null;
3326
- // WebRTC is provided externally or via collection
3327
3324
  default:
3328
3325
  return null;
3329
3326
  }
@@ -3337,52 +3334,9 @@ var PipelineAddon = class {
3337
3334
  if (!this.recordingDb) throw new Error("PipelineAddon recording not initialized");
3338
3335
  return this.recordingDb;
3339
3336
  }
3340
- /** Whether the analysis pipeline package loaded successfully */
3341
- isAnalysisAvailable() {
3342
- return this.analysisPipeline !== null;
3343
- }
3344
3337
  // --- IConfigurable ---
3345
3338
  getConfigSchema() {
3346
- return {
3347
- sections: [
3348
- {
3349
- id: "ffmpeg",
3350
- title: "FFmpeg Settings",
3351
- columns: 2,
3352
- fields: [
3353
- {
3354
- type: "text",
3355
- key: "ffmpegPath",
3356
- label: "FFmpeg Binary Path",
3357
- description: 'Path to the ffmpeg executable, or just "ffmpeg" if it is in your PATH',
3358
- placeholder: "ffmpeg"
3359
- },
3360
- {
3361
- type: "select",
3362
- key: "hwaccel",
3363
- label: "Hardware Acceleration",
3364
- description: "Enable GPU-accelerated video encoding if supported by your hardware",
3365
- options: [
3366
- { value: "", label: "None (software)", description: "CPU-only encoding" },
3367
- { value: "nvenc", label: "NVIDIA NVENC", description: "NVIDIA GPU encoding" },
3368
- { value: "vaapi", label: "Intel VAAPI", description: "Intel GPU encoding (Linux)" },
3369
- { value: "videotoolbox", label: "Apple VideoToolbox", description: "macOS hardware encoding" }
3370
- ]
3371
- },
3372
- {
3373
- type: "number",
3374
- key: "threads",
3375
- label: "FFmpeg Threads",
3376
- description: "Number of CPU threads for software encoding (0 = auto)",
3377
- min: 0,
3378
- max: 64,
3379
- step: 1,
3380
- unit: "threads"
3381
- }
3382
- ]
3383
- }
3384
- ]
3385
- };
3339
+ return { sections: [] };
3386
3340
  }
3387
3341
  getConfig() {
3388
3342
  return { ...this.currentRecordingConfig };
@@ -3391,7 +3345,9 @@ var PipelineAddon = class {
3391
3345
  this.currentRecordingConfig = {
3392
3346
  ffmpegPath: config.ffmpegPath ?? this.currentRecordingConfig.ffmpegPath,
3393
3347
  hwaccel: config.hwaccel ?? this.currentRecordingConfig.hwaccel,
3394
- threads: config.threads ?? this.currentRecordingConfig.threads
3348
+ threads: config.threads ?? this.currentRecordingConfig.threads,
3349
+ segmentDurationSeconds: config.segmentDurationSeconds ?? this.currentRecordingConfig.segmentDurationSeconds,
3350
+ defaultRetentionDays: config.defaultRetentionDays ?? this.currentRecordingConfig.defaultRetentionDays
3395
3351
  };
3396
3352
  }
3397
3353
  };