@camstack/addon-pipeline 0.1.0

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.
@@ -0,0 +1,238 @@
1
+ import Database from 'better-sqlite3';
2
+ import { FfmpegConfig, INetworkQualityTracker, IScopedLogger, IEventBus, IStreamingEngine, IPipelineManager, IFileStorage, ICamstackAddon, IConfigurable, AddonManifest, AddonContext, CapabilityProviderMap, ConfigUISchema } from '@camstack/types';
3
+
4
+ interface RecordingPolicy {
5
+ readonly deviceId: string;
6
+ readonly mode: RecordingMode;
7
+ readonly streams: readonly StreamPolicy[];
8
+ readonly enabled: boolean;
9
+ readonly preBufferSec: number;
10
+ readonly postBufferSec: number;
11
+ readonly scheduleRules?: readonly ScheduleRule[];
12
+ }
13
+ type RecordingMode = 'continuous' | 'motion' | 'scheduled' | 'composite';
14
+ interface StreamPolicy {
15
+ readonly streamId: string;
16
+ readonly mode: 'always' | 'inherit';
17
+ }
18
+ interface ScheduleRule {
19
+ readonly days: readonly number[];
20
+ readonly startTime: string;
21
+ readonly endTime: string;
22
+ readonly mode: 'continuous' | 'motion';
23
+ }
24
+ interface RecordingStorageConfig {
25
+ readonly deviceId: string;
26
+ readonly dataCategory: DataCategory;
27
+ readonly storageName: string;
28
+ readonly subDirectory: string;
29
+ readonly retentionDays: number | null;
30
+ readonly retentionGb: number | null;
31
+ }
32
+ type DataCategory = 'recording:main' | 'recording:mid' | 'recording:sub' | 'thumbnail:scrub' | 'thumbnail:event';
33
+ interface RecordingSegment {
34
+ readonly id: string;
35
+ readonly deviceId: string;
36
+ readonly streamId: string;
37
+ readonly startTime: number;
38
+ readonly endTime: number;
39
+ readonly duration: number;
40
+ readonly path: string;
41
+ readonly storageName: string;
42
+ readonly subDirectory: string;
43
+ readonly sizeBytes: number;
44
+ readonly codec: 'h264' | 'h265';
45
+ readonly hasAudio: boolean;
46
+ }
47
+ interface RecordingThumbnail {
48
+ readonly deviceId: string;
49
+ readonly timestamp: number;
50
+ readonly path: string;
51
+ readonly storageName: string;
52
+ readonly subDirectory: string;
53
+ readonly sizeBytes: number;
54
+ readonly category: 'scrub' | 'event';
55
+ }
56
+ type CleanupStatus = 'pending' | 'in_progress' | 'completed' | 'cancelled';
57
+ interface CleanupQueueEntry {
58
+ readonly deviceId: string;
59
+ readonly disabledAt: number;
60
+ readonly cleanupAfter: number;
61
+ readonly status: CleanupStatus;
62
+ readonly startedAt: number | null;
63
+ }
64
+ type SegmentWriterState = 'idle' | 'recording' | 'rotating' | 'stopping';
65
+ interface StreamEstimate {
66
+ readonly bitrateKbps: number;
67
+ readonly retentionDays: number | null;
68
+ readonly retentionGb: number | null;
69
+ readonly estimatedGb: number;
70
+ readonly estimatedDaysAtCapacity: number | null;
71
+ }
72
+ interface StorageEstimate {
73
+ readonly perStream: Readonly<Record<string, StreamEstimate>>;
74
+ readonly thumbnails: {
75
+ readonly estimatedGb: number;
76
+ };
77
+ readonly totalEstimatedGb: number;
78
+ readonly motionEstimate?: {
79
+ readonly avgEventsPerDay: number;
80
+ readonly avgDurationSec: number;
81
+ readonly dutyCyclePercent: number;
82
+ };
83
+ }
84
+ interface RecordingEnableConfig {
85
+ readonly policy: Omit<RecordingPolicy, 'deviceId'>;
86
+ readonly storageOverrides?: readonly Omit<RecordingStorageConfig, 'deviceId'>[];
87
+ readonly ffmpegOverrides?: Partial<FfmpegConfig>;
88
+ }
89
+
90
+ interface StorageUsage {
91
+ readonly totalBytes: number;
92
+ readonly segmentCount: number;
93
+ }
94
+ interface AvailabilityRange {
95
+ readonly startTime: number;
96
+ readonly endTime: number;
97
+ readonly streams: readonly string[];
98
+ }
99
+ declare class RecordingDb$1 {
100
+ private readonly db;
101
+ constructor(db: Database.Database);
102
+ initialize(): void;
103
+ insertSegment(seg: RecordingSegment): void;
104
+ querySegments(deviceId: string, streamId: string, startTime: number, endTime: number): readonly RecordingSegment[];
105
+ deleteSegmentsBefore(deviceId: string, streamId: string, beforeTime: number): readonly RecordingSegment[];
106
+ deleteSegmentsForDevice(deviceId: string): readonly RecordingSegment[];
107
+ getStorageUsage(deviceId: string, streamId: string): StorageUsage;
108
+ getOldestSegments(deviceId: string, streamId: string, limit: number): readonly RecordingSegment[];
109
+ getAvailability(deviceId: string, startTime: number, endTime: number): readonly AvailabilityRange[];
110
+ getMotionStats(deviceId: string, startTime: number, endTime: number): {
111
+ readonly totalEvents: number;
112
+ readonly avgDurationSec: number;
113
+ readonly avgEventsPerDay: number;
114
+ readonly dutyCyclePercent: number;
115
+ };
116
+ insertThumbnail(thumb: RecordingThumbnail): void;
117
+ findNearestThumbnail(deviceId: string, timestamp: number, category: string): RecordingThumbnail | null;
118
+ deleteThumbnailsBefore(deviceId: string, beforeTime: number): number;
119
+ deleteThumbnailsForDevice(deviceId: string): number;
120
+ upsertPolicy(policy: {
121
+ deviceId: string;
122
+ enabled: boolean;
123
+ mode: RecordingMode;
124
+ streams: readonly StreamPolicy[];
125
+ preBufferSec: number;
126
+ postBufferSec: number;
127
+ scheduleRules?: readonly ScheduleRule[];
128
+ }): void;
129
+ getPolicy(deviceId: string): RecordingPolicy | null;
130
+ getEnabledPolicies(): readonly RecordingPolicy[];
131
+ deletePolicy(deviceId: string): void;
132
+ upsertStorageConfig(config: RecordingStorageConfig): void;
133
+ resolveStorageConfig(deviceId: string, category: DataCategory): RecordingStorageConfig | null;
134
+ addToCleanupQueue(deviceId: string, disabledAt: number): void;
135
+ cancelCleanup(deviceId: string): void;
136
+ getCleanupEntry(deviceId: string): CleanupQueueEntry | null;
137
+ getPendingCleanups(): readonly CleanupQueueEntry[];
138
+ resetStaleCleanups(maxAgeMs?: number): void;
139
+ markCleanupInProgress(deviceId: string): void;
140
+ markCleanupCompleted(deviceId: string): void;
141
+ }
142
+
143
+ interface PlaylistOptions {
144
+ readonly live?: boolean;
145
+ }
146
+ declare class PlaylistGenerator {
147
+ private readonly db;
148
+ constructor(db: RecordingDb$1);
149
+ generate(deviceId: string, streamId: string, startTime: number, endTime: number, options?: PlaylistOptions): string;
150
+ private resolveSegments;
151
+ private buildPlaylist;
152
+ }
153
+
154
+ interface MotionEstimateInput {
155
+ readonly avgEventsPerDay: number;
156
+ readonly avgDurationSec: number;
157
+ }
158
+ declare class StorageEstimator {
159
+ private readonly db;
160
+ private readonly networkTracker;
161
+ constructor(db: RecordingDb$1, networkTracker: INetworkQualityTracker);
162
+ estimateForDevice(deviceId: string, motionInput?: MotionEstimateInput): StorageEstimate;
163
+ }
164
+
165
+ interface RecordingCoordinatorConfig {
166
+ readonly db: RecordingDb$1;
167
+ readonly logger: IScopedLogger;
168
+ readonly eventBus: IEventBus;
169
+ readonly streamingEngine: IStreamingEngine;
170
+ readonly pipelineManager: IPipelineManager;
171
+ readonly networkTracker: INetworkQualityTracker;
172
+ readonly fileStorage: IFileStorage;
173
+ readonly storagePath: string;
174
+ readonly globalFfmpegConfig: Partial<FfmpegConfig>;
175
+ readonly detectedFfmpegConfig: Partial<FfmpegConfig>;
176
+ }
177
+ declare class RecordingCoordinator$1 {
178
+ private readonly db;
179
+ private readonly logger;
180
+ private readonly eventBus;
181
+ private readonly streamingEngine;
182
+ private readonly pipelineManager;
183
+ private readonly networkTracker;
184
+ private readonly fileStorage;
185
+ private readonly storagePath;
186
+ private readonly globalFfmpegConfig;
187
+ private readonly detectedFfmpegConfig;
188
+ private readonly recordings;
189
+ private policyTimer;
190
+ private readonly retentionManager;
191
+ readonly playlistGenerator: PlaylistGenerator;
192
+ readonly storageEstimator: StorageEstimator;
193
+ constructor(config: RecordingCoordinatorConfig);
194
+ start(): Promise<void>;
195
+ stop(): void;
196
+ enableRecording(deviceId: string, config: RecordingEnableConfig): Promise<void>;
197
+ disableRecording(deviceId: string): Promise<void>;
198
+ isRecording(deviceId: string): boolean;
199
+ evaluatePolicies(): void;
200
+ static evaluateScheduleRule(rule: ScheduleRule, date: Date): boolean;
201
+ private subscribeToMotionEvents;
202
+ private handleMotionEvent;
203
+ private stopRecordingInternal;
204
+ }
205
+
206
+ type RecordingCoordinator = RecordingCoordinator$1;
207
+ type RecordingDb = RecordingDb$1;
208
+ interface RecordingEngineDependencies {
209
+ readonly streamingEngine: IStreamingEngine;
210
+ readonly pipelineManager: IPipelineManager;
211
+ readonly networkTracker: INetworkQualityTracker;
212
+ }
213
+ type RecordingEngineV2Dependencies = RecordingEngineDependencies;
214
+ declare class PipelineAddon implements ICamstackAddon, IConfigurable {
215
+ readonly manifest: AddonManifest;
216
+ private brokerManager;
217
+ private coordinator;
218
+ private recordingDb;
219
+ private sqliteDb;
220
+ private recordingDeps;
221
+ private currentRecordingConfig;
222
+ private analysisPipeline;
223
+ private analysisLogger;
224
+ private persistenceFacade;
225
+ setRecordingDependencies(deps: RecordingEngineDependencies): void;
226
+ initialize(context: AddonContext): Promise<void>;
227
+ shutdown(): Promise<void>;
228
+ getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
229
+ getCoordinator(): RecordingCoordinator;
230
+ getRecordingDb(): RecordingDb;
231
+ /** Whether the analysis pipeline package loaded successfully */
232
+ isAnalysisAvailable(): boolean;
233
+ getConfigSchema(): ConfigUISchema;
234
+ getConfig(): Record<string, unknown>;
235
+ onConfigChange(config: Record<string, unknown>): Promise<void>;
236
+ }
237
+
238
+ export { type AvailabilityRange as A, type CleanupQueueEntry as C, type DataCategory as D, PipelineAddon as P, RecordingCoordinator$1 as R, type ScheduleRule as S, type CleanupStatus as a, RecordingDb$1 as b, type RecordingEnableConfig as c, type RecordingEngineDependencies as d, type RecordingEngineV2Dependencies as e, type RecordingMode as f, type RecordingPolicy as g, type RecordingSegment as h, type RecordingStorageConfig as i, type RecordingThumbnail as j, type SegmentWriterState as k, type StorageEstimate as l, type StorageUsage as m, type StreamEstimate as n, type StreamPolicy as o };
@@ -0,0 +1,238 @@
1
+ import Database from 'better-sqlite3';
2
+ import { FfmpegConfig, INetworkQualityTracker, IScopedLogger, IEventBus, IStreamingEngine, IPipelineManager, IFileStorage, ICamstackAddon, IConfigurable, AddonManifest, AddonContext, CapabilityProviderMap, ConfigUISchema } from '@camstack/types';
3
+
4
+ interface RecordingPolicy {
5
+ readonly deviceId: string;
6
+ readonly mode: RecordingMode;
7
+ readonly streams: readonly StreamPolicy[];
8
+ readonly enabled: boolean;
9
+ readonly preBufferSec: number;
10
+ readonly postBufferSec: number;
11
+ readonly scheduleRules?: readonly ScheduleRule[];
12
+ }
13
+ type RecordingMode = 'continuous' | 'motion' | 'scheduled' | 'composite';
14
+ interface StreamPolicy {
15
+ readonly streamId: string;
16
+ readonly mode: 'always' | 'inherit';
17
+ }
18
+ interface ScheduleRule {
19
+ readonly days: readonly number[];
20
+ readonly startTime: string;
21
+ readonly endTime: string;
22
+ readonly mode: 'continuous' | 'motion';
23
+ }
24
+ interface RecordingStorageConfig {
25
+ readonly deviceId: string;
26
+ readonly dataCategory: DataCategory;
27
+ readonly storageName: string;
28
+ readonly subDirectory: string;
29
+ readonly retentionDays: number | null;
30
+ readonly retentionGb: number | null;
31
+ }
32
+ type DataCategory = 'recording:main' | 'recording:mid' | 'recording:sub' | 'thumbnail:scrub' | 'thumbnail:event';
33
+ interface RecordingSegment {
34
+ readonly id: string;
35
+ readonly deviceId: string;
36
+ readonly streamId: string;
37
+ readonly startTime: number;
38
+ readonly endTime: number;
39
+ readonly duration: number;
40
+ readonly path: string;
41
+ readonly storageName: string;
42
+ readonly subDirectory: string;
43
+ readonly sizeBytes: number;
44
+ readonly codec: 'h264' | 'h265';
45
+ readonly hasAudio: boolean;
46
+ }
47
+ interface RecordingThumbnail {
48
+ readonly deviceId: string;
49
+ readonly timestamp: number;
50
+ readonly path: string;
51
+ readonly storageName: string;
52
+ readonly subDirectory: string;
53
+ readonly sizeBytes: number;
54
+ readonly category: 'scrub' | 'event';
55
+ }
56
+ type CleanupStatus = 'pending' | 'in_progress' | 'completed' | 'cancelled';
57
+ interface CleanupQueueEntry {
58
+ readonly deviceId: string;
59
+ readonly disabledAt: number;
60
+ readonly cleanupAfter: number;
61
+ readonly status: CleanupStatus;
62
+ readonly startedAt: number | null;
63
+ }
64
+ type SegmentWriterState = 'idle' | 'recording' | 'rotating' | 'stopping';
65
+ interface StreamEstimate {
66
+ readonly bitrateKbps: number;
67
+ readonly retentionDays: number | null;
68
+ readonly retentionGb: number | null;
69
+ readonly estimatedGb: number;
70
+ readonly estimatedDaysAtCapacity: number | null;
71
+ }
72
+ interface StorageEstimate {
73
+ readonly perStream: Readonly<Record<string, StreamEstimate>>;
74
+ readonly thumbnails: {
75
+ readonly estimatedGb: number;
76
+ };
77
+ readonly totalEstimatedGb: number;
78
+ readonly motionEstimate?: {
79
+ readonly avgEventsPerDay: number;
80
+ readonly avgDurationSec: number;
81
+ readonly dutyCyclePercent: number;
82
+ };
83
+ }
84
+ interface RecordingEnableConfig {
85
+ readonly policy: Omit<RecordingPolicy, 'deviceId'>;
86
+ readonly storageOverrides?: readonly Omit<RecordingStorageConfig, 'deviceId'>[];
87
+ readonly ffmpegOverrides?: Partial<FfmpegConfig>;
88
+ }
89
+
90
+ interface StorageUsage {
91
+ readonly totalBytes: number;
92
+ readonly segmentCount: number;
93
+ }
94
+ interface AvailabilityRange {
95
+ readonly startTime: number;
96
+ readonly endTime: number;
97
+ readonly streams: readonly string[];
98
+ }
99
+ declare class RecordingDb$1 {
100
+ private readonly db;
101
+ constructor(db: Database.Database);
102
+ initialize(): void;
103
+ insertSegment(seg: RecordingSegment): void;
104
+ querySegments(deviceId: string, streamId: string, startTime: number, endTime: number): readonly RecordingSegment[];
105
+ deleteSegmentsBefore(deviceId: string, streamId: string, beforeTime: number): readonly RecordingSegment[];
106
+ deleteSegmentsForDevice(deviceId: string): readonly RecordingSegment[];
107
+ getStorageUsage(deviceId: string, streamId: string): StorageUsage;
108
+ getOldestSegments(deviceId: string, streamId: string, limit: number): readonly RecordingSegment[];
109
+ getAvailability(deviceId: string, startTime: number, endTime: number): readonly AvailabilityRange[];
110
+ getMotionStats(deviceId: string, startTime: number, endTime: number): {
111
+ readonly totalEvents: number;
112
+ readonly avgDurationSec: number;
113
+ readonly avgEventsPerDay: number;
114
+ readonly dutyCyclePercent: number;
115
+ };
116
+ insertThumbnail(thumb: RecordingThumbnail): void;
117
+ findNearestThumbnail(deviceId: string, timestamp: number, category: string): RecordingThumbnail | null;
118
+ deleteThumbnailsBefore(deviceId: string, beforeTime: number): number;
119
+ deleteThumbnailsForDevice(deviceId: string): number;
120
+ upsertPolicy(policy: {
121
+ deviceId: string;
122
+ enabled: boolean;
123
+ mode: RecordingMode;
124
+ streams: readonly StreamPolicy[];
125
+ preBufferSec: number;
126
+ postBufferSec: number;
127
+ scheduleRules?: readonly ScheduleRule[];
128
+ }): void;
129
+ getPolicy(deviceId: string): RecordingPolicy | null;
130
+ getEnabledPolicies(): readonly RecordingPolicy[];
131
+ deletePolicy(deviceId: string): void;
132
+ upsertStorageConfig(config: RecordingStorageConfig): void;
133
+ resolveStorageConfig(deviceId: string, category: DataCategory): RecordingStorageConfig | null;
134
+ addToCleanupQueue(deviceId: string, disabledAt: number): void;
135
+ cancelCleanup(deviceId: string): void;
136
+ getCleanupEntry(deviceId: string): CleanupQueueEntry | null;
137
+ getPendingCleanups(): readonly CleanupQueueEntry[];
138
+ resetStaleCleanups(maxAgeMs?: number): void;
139
+ markCleanupInProgress(deviceId: string): void;
140
+ markCleanupCompleted(deviceId: string): void;
141
+ }
142
+
143
+ interface PlaylistOptions {
144
+ readonly live?: boolean;
145
+ }
146
+ declare class PlaylistGenerator {
147
+ private readonly db;
148
+ constructor(db: RecordingDb$1);
149
+ generate(deviceId: string, streamId: string, startTime: number, endTime: number, options?: PlaylistOptions): string;
150
+ private resolveSegments;
151
+ private buildPlaylist;
152
+ }
153
+
154
+ interface MotionEstimateInput {
155
+ readonly avgEventsPerDay: number;
156
+ readonly avgDurationSec: number;
157
+ }
158
+ declare class StorageEstimator {
159
+ private readonly db;
160
+ private readonly networkTracker;
161
+ constructor(db: RecordingDb$1, networkTracker: INetworkQualityTracker);
162
+ estimateForDevice(deviceId: string, motionInput?: MotionEstimateInput): StorageEstimate;
163
+ }
164
+
165
+ interface RecordingCoordinatorConfig {
166
+ readonly db: RecordingDb$1;
167
+ readonly logger: IScopedLogger;
168
+ readonly eventBus: IEventBus;
169
+ readonly streamingEngine: IStreamingEngine;
170
+ readonly pipelineManager: IPipelineManager;
171
+ readonly networkTracker: INetworkQualityTracker;
172
+ readonly fileStorage: IFileStorage;
173
+ readonly storagePath: string;
174
+ readonly globalFfmpegConfig: Partial<FfmpegConfig>;
175
+ readonly detectedFfmpegConfig: Partial<FfmpegConfig>;
176
+ }
177
+ declare class RecordingCoordinator$1 {
178
+ private readonly db;
179
+ private readonly logger;
180
+ private readonly eventBus;
181
+ private readonly streamingEngine;
182
+ private readonly pipelineManager;
183
+ private readonly networkTracker;
184
+ private readonly fileStorage;
185
+ private readonly storagePath;
186
+ private readonly globalFfmpegConfig;
187
+ private readonly detectedFfmpegConfig;
188
+ private readonly recordings;
189
+ private policyTimer;
190
+ private readonly retentionManager;
191
+ readonly playlistGenerator: PlaylistGenerator;
192
+ readonly storageEstimator: StorageEstimator;
193
+ constructor(config: RecordingCoordinatorConfig);
194
+ start(): Promise<void>;
195
+ stop(): void;
196
+ enableRecording(deviceId: string, config: RecordingEnableConfig): Promise<void>;
197
+ disableRecording(deviceId: string): Promise<void>;
198
+ isRecording(deviceId: string): boolean;
199
+ evaluatePolicies(): void;
200
+ static evaluateScheduleRule(rule: ScheduleRule, date: Date): boolean;
201
+ private subscribeToMotionEvents;
202
+ private handleMotionEvent;
203
+ private stopRecordingInternal;
204
+ }
205
+
206
+ type RecordingCoordinator = RecordingCoordinator$1;
207
+ type RecordingDb = RecordingDb$1;
208
+ interface RecordingEngineDependencies {
209
+ readonly streamingEngine: IStreamingEngine;
210
+ readonly pipelineManager: IPipelineManager;
211
+ readonly networkTracker: INetworkQualityTracker;
212
+ }
213
+ type RecordingEngineV2Dependencies = RecordingEngineDependencies;
214
+ declare class PipelineAddon implements ICamstackAddon, IConfigurable {
215
+ readonly manifest: AddonManifest;
216
+ private brokerManager;
217
+ private coordinator;
218
+ private recordingDb;
219
+ private sqliteDb;
220
+ private recordingDeps;
221
+ private currentRecordingConfig;
222
+ private analysisPipeline;
223
+ private analysisLogger;
224
+ private persistenceFacade;
225
+ setRecordingDependencies(deps: RecordingEngineDependencies): void;
226
+ initialize(context: AddonContext): Promise<void>;
227
+ shutdown(): Promise<void>;
228
+ getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
229
+ getCoordinator(): RecordingCoordinator;
230
+ getRecordingDb(): RecordingDb;
231
+ /** Whether the analysis pipeline package loaded successfully */
232
+ isAnalysisAvailable(): boolean;
233
+ getConfigSchema(): ConfigUISchema;
234
+ getConfig(): Record<string, unknown>;
235
+ onConfigChange(config: Record<string, unknown>): Promise<void>;
236
+ }
237
+
238
+ export { type AvailabilityRange as A, type CleanupQueueEntry as C, type DataCategory as D, PipelineAddon as P, RecordingCoordinator$1 as R, type ScheduleRule as S, type CleanupStatus as a, RecordingDb$1 as b, type RecordingEnableConfig as c, type RecordingEngineDependencies as d, type RecordingEngineV2Dependencies as e, type RecordingMode as f, type RecordingPolicy as g, type RecordingSegment as h, type RecordingStorageConfig as i, type RecordingThumbnail as j, type SegmentWriterState as k, type StorageEstimate as l, type StorageUsage as m, type StreamEstimate as n, type StreamPolicy as o };