@camstack/addon-pipeline 0.1.0 → 0.1.2

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,6 +79,12 @@ function buildFfmpegOutputArgs(config) {
78
79
  if (config.outputArgs?.length) args.push(...config.outputArgs);
79
80
  return args;
80
81
  }
82
+ async function resolveFfmpegBinary(configPath, deps) {
83
+ if (configPath && configPath !== "ffmpeg") {
84
+ return configPath;
85
+ }
86
+ return deps.ensureFfmpeg();
87
+ }
81
88
  var import_node_child_process3, os;
82
89
  var init_ffmpeg_config = __esm({
83
90
  "src/recording/ffmpeg-config.ts"() {
@@ -1189,7 +1196,7 @@ var recording_coordinator_exports = {};
1189
1196
  __export(recording_coordinator_exports, {
1190
1197
  RecordingCoordinator: () => RecordingCoordinator
1191
1198
  });
1192
- var POLICY_EVAL_INTERVAL_MS, MOTION_FALLBACK_TIMEOUT_MS, RecordingCoordinator;
1199
+ var DEFAULT_SEGMENT_DURATION_SEC, POLICY_EVAL_INTERVAL_MS, MOTION_FALLBACK_TIMEOUT_MS, RecordingCoordinator;
1193
1200
  var init_recording_coordinator = __esm({
1194
1201
  "src/recording/recording-coordinator.ts"() {
1195
1202
  "use strict";
@@ -1199,6 +1206,7 @@ var init_recording_coordinator = __esm({
1199
1206
  init_retention_manager();
1200
1207
  init_playlist_generator();
1201
1208
  init_storage_estimator();
1209
+ DEFAULT_SEGMENT_DURATION_SEC = 4;
1202
1210
  POLICY_EVAL_INTERVAL_MS = 1e3;
1203
1211
  MOTION_FALLBACK_TIMEOUT_MS = 6e4;
1204
1212
  RecordingCoordinator = class _RecordingCoordinator {
@@ -1212,6 +1220,7 @@ var init_recording_coordinator = __esm({
1212
1220
  storagePath;
1213
1221
  globalFfmpegConfig;
1214
1222
  detectedFfmpegConfig;
1223
+ segmentDurationSec;
1215
1224
  recordings = /* @__PURE__ */ new Map();
1216
1225
  policyTimer = null;
1217
1226
  retentionManager;
@@ -1228,6 +1237,7 @@ var init_recording_coordinator = __esm({
1228
1237
  this.storagePath = config.storagePath;
1229
1238
  this.globalFfmpegConfig = config.globalFfmpegConfig;
1230
1239
  this.detectedFfmpegConfig = config.detectedFfmpegConfig;
1240
+ this.segmentDurationSec = config.segmentDurationSec ?? DEFAULT_SEGMENT_DURATION_SEC;
1231
1241
  this.retentionManager = new RetentionManager(
1232
1242
  this.db,
1233
1243
  this.logger.child("retention"),
@@ -1313,7 +1323,7 @@ var init_recording_coordinator = __esm({
1313
1323
  const writerConfig = {
1314
1324
  deviceId,
1315
1325
  streamId: sp.streamId,
1316
- segmentDurationSec: 4,
1326
+ segmentDurationSec: this.segmentDurationSec,
1317
1327
  storagePath: this.storagePath,
1318
1328
  storageName,
1319
1329
  subDirectory,
@@ -3166,9 +3176,7 @@ var PipelineAddon = class {
3166
3176
  capabilities: [
3167
3177
  { name: "stream-broker", mode: "singleton" },
3168
3178
  { name: "recording-engine", mode: "singleton" },
3169
- { name: "analysis-pipeline", mode: "singleton" },
3170
- { name: "analysis-data-persistence", mode: "singleton" },
3171
- { name: "webrtc", mode: "collection" }
3179
+ { name: "analysis-data-persistence", mode: "singleton" }
3172
3180
  ]
3173
3181
  };
3174
3182
  // Stream broker
@@ -3181,11 +3189,10 @@ var PipelineAddon = class {
3181
3189
  currentRecordingConfig = {
3182
3190
  ffmpegPath: "ffmpeg",
3183
3191
  hwaccel: void 0,
3184
- threads: void 0
3192
+ threads: void 0,
3193
+ segmentDurationSeconds: 4,
3194
+ defaultRetentionDays: 30
3185
3195
  };
3186
- // Analysis pipeline
3187
- analysisPipeline = null;
3188
- analysisLogger = null;
3189
3196
  // Analysis persistence
3190
3197
  persistenceFacade = null;
3191
3198
  setRecordingDependencies(deps) {
@@ -3215,10 +3222,14 @@ var PipelineAddon = class {
3215
3222
  hwaccel: context.addonConfig.hwaccel ?? this.currentRecordingConfig.hwaccel,
3216
3223
  threads: context.addonConfig.threads ?? this.currentRecordingConfig.threads
3217
3224
  };
3225
+ const segmentDurationSeconds = context.addonConfig.segmentDurationSeconds ?? this.currentRecordingConfig.segmentDurationSeconds;
3226
+ const defaultRetentionDays = context.addonConfig.defaultRetentionDays ?? this.currentRecordingConfig.defaultRetentionDays;
3218
3227
  this.currentRecordingConfig = {
3219
3228
  ffmpegPath,
3220
3229
  hwaccel: globalFfmpegConfig.hwaccel,
3221
- threads: globalFfmpegConfig.threads
3230
+ threads: globalFfmpegConfig.threads,
3231
+ segmentDurationSeconds,
3232
+ defaultRetentionDays
3222
3233
  };
3223
3234
  const fileStorage = context.storage.files;
3224
3235
  if (!fileStorage) {
@@ -3234,7 +3245,8 @@ var PipelineAddon = class {
3234
3245
  fileStorage,
3235
3246
  storagePath,
3236
3247
  globalFfmpegConfig,
3237
- detectedFfmpegConfig
3248
+ detectedFfmpegConfig,
3249
+ segmentDurationSec: segmentDurationSeconds
3238
3250
  });
3239
3251
  await this.coordinator.start();
3240
3252
  context.logger.info("Recording Engine initialized");
@@ -3243,16 +3255,6 @@ var PipelineAddon = class {
3243
3255
  context.logger.warn(`Recording Engine failed to initialize: ${msg}`);
3244
3256
  }
3245
3257
  }
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
3258
  const eventPersistence = new EventPersistenceService({
3257
3259
  getStorageLocation: () => context.storage,
3258
3260
  subscribe: (filter, handler) => context.eventBus.subscribe(filter, handler),
@@ -3304,7 +3306,6 @@ var PipelineAddon = class {
3304
3306
  this.sqliteDb = null;
3305
3307
  }
3306
3308
  this.recordingDb = null;
3307
- this.analysisPipeline = null;
3308
3309
  if (this.persistenceFacade) {
3309
3310
  this.persistenceFacade.eventPersistence.stop();
3310
3311
  this.persistenceFacade.retention.stop();
@@ -3317,13 +3318,8 @@ var PipelineAddon = class {
3317
3318
  return this.brokerManager;
3318
3319
  case "recording-engine":
3319
3320
  return this.coordinator;
3320
- case "analysis-pipeline":
3321
- return this.analysisPipeline;
3322
3321
  case "analysis-data-persistence":
3323
3322
  return this.persistenceFacade;
3324
- case "webrtc":
3325
- return null;
3326
- // WebRTC is provided externally or via collection
3327
3323
  default:
3328
3324
  return null;
3329
3325
  }
@@ -3337,52 +3333,9 @@ var PipelineAddon = class {
3337
3333
  if (!this.recordingDb) throw new Error("PipelineAddon recording not initialized");
3338
3334
  return this.recordingDb;
3339
3335
  }
3340
- /** Whether the analysis pipeline package loaded successfully */
3341
- isAnalysisAvailable() {
3342
- return this.analysisPipeline !== null;
3343
- }
3344
3336
  // --- IConfigurable ---
3345
3337
  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
- };
3338
+ return { sections: [] };
3386
3339
  }
3387
3340
  getConfig() {
3388
3341
  return { ...this.currentRecordingConfig };
@@ -3391,7 +3344,9 @@ var PipelineAddon = class {
3391
3344
  this.currentRecordingConfig = {
3392
3345
  ffmpegPath: config.ffmpegPath ?? this.currentRecordingConfig.ffmpegPath,
3393
3346
  hwaccel: config.hwaccel ?? this.currentRecordingConfig.hwaccel,
3394
- threads: config.threads ?? this.currentRecordingConfig.threads
3347
+ threads: config.threads ?? this.currentRecordingConfig.threads,
3348
+ segmentDurationSeconds: config.segmentDurationSeconds ?? this.currentRecordingConfig.segmentDurationSeconds,
3349
+ defaultRetentionDays: config.defaultRetentionDays ?? this.currentRecordingConfig.defaultRetentionDays
3395
3350
  };
3396
3351
  }
3397
3352
  };