@camstack/addon-post-analysis 0.1.19 → 0.1.20

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.
Files changed (67) hide show
  1. package/dist/embedding-encoder/index.js +1 -1
  2. package/dist/embedding-encoder/index.mjs +1 -1
  3. package/dist/enrichment-engine/index.js +1 -1
  4. package/dist/enrichment-engine/index.mjs +1 -1
  5. package/dist/{index-BFbwYH1P.js → index-B0RhVv1c.js} +3514 -750
  6. package/dist/index-B0RhVv1c.js.map +1 -0
  7. package/dist/{index-BrTlzsrE.mjs → index-ot5PeFg_.mjs} +3517 -753
  8. package/dist/index-ot5PeFg_.mjs.map +1 -0
  9. package/dist/pipeline-analytics/@mf-types.zip +0 -0
  10. package/dist/pipeline-analytics/{__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_sdk__loadShare__.mjs-h5aXOPSA.mjs → __mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_sdk__loadShare__.mjs-lantnv8e.mjs} +1 -1
  11. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-BD3oMNGB.mjs +29 -0
  12. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-BgOHCakr.mjs +18 -0
  13. package/dist/pipeline-analytics/{__mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.mjs-BZTB2scQ.mjs → __mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.mjs-D1qPKjvR.mjs} +2 -1
  14. package/dist/pipeline-analytics/{__mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-CJO5YKGV.mjs → __mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-B5X50Xa4.mjs} +1 -1
  15. package/dist/pipeline-analytics/{__mfe_internal__addon_pipeline_analytics_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-B0h0AGOH.mjs → __mfe_internal__addon_pipeline_analytics_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-B10b5k5J.mjs} +1 -1
  16. package/dist/pipeline-analytics/_stub.js +2 -3
  17. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-kZBmgzMg.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DWB3apaJ.mjs} +6 -6
  18. package/dist/pipeline-analytics/{client-BlxIUpgf.mjs → client-C6xdgLZU.mjs} +2 -2
  19. package/dist/pipeline-analytics/{hostInit-qBB1Thhi.mjs → hostInit-3cyL9eyG.mjs} +12 -12
  20. package/dist/pipeline-analytics/{index-Dw6Q30NI.mjs → index-BCTHeI2m.mjs} +253 -267
  21. package/dist/pipeline-analytics/{index-DlhiA9R0.mjs → index-BuWLz0GG.mjs} +1 -1
  22. package/dist/pipeline-analytics/{index-DtdgkNgf.mjs → index-CIwq-tQL.mjs} +1 -1
  23. package/dist/pipeline-analytics/{index-BoL0rgZt.mjs → index-CWBMDbou.mjs} +1 -1
  24. package/dist/pipeline-analytics/index-CZhagnlH.mjs +67784 -0
  25. package/dist/pipeline-analytics/{index-CR1aiZDH.mjs → index-D883Q5B8.mjs} +1 -1
  26. package/dist/pipeline-analytics/{index-Dy2V7VOm.mjs → index-DtOI1aTU.mjs} +10112 -5987
  27. package/dist/pipeline-analytics/index.js +605 -42
  28. package/dist/pipeline-analytics/index.js.map +1 -1
  29. package/dist/pipeline-analytics/index.mjs +604 -42
  30. package/dist/pipeline-analytics/index.mjs.map +1 -1
  31. package/dist/pipeline-analytics/{jsx-runtime-Dlbl3gpr.mjs → jsx-runtime-DdLhuHmJ.mjs} +1 -1
  32. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  33. package/dist/pipeline-analytics/{schemas-ClCuS4qa.mjs → schemas-B7L0qZtq.mjs} +411 -406
  34. package/package.json +12 -27
  35. package/dist/ffmpeg-config-DRONlBsj.mjs +0 -56
  36. package/dist/ffmpeg-config-DRONlBsj.mjs.map +0 -1
  37. package/dist/ffmpeg-config-uANz3sV5.js +0 -73
  38. package/dist/ffmpeg-config-uANz3sV5.js.map +0 -1
  39. package/dist/index-BFbwYH1P.js.map +0 -1
  40. package/dist/index-BrTlzsrE.mjs.map +0 -1
  41. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-NjF4kxzW.mjs +0 -19
  42. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-7HAAnpQu.mjs +0 -18
  43. package/dist/pipeline-analytics/index-i47purqY.mjs +0 -37880
  44. package/dist/playlist-generator-EhPaB7Hn.js +0 -48
  45. package/dist/playlist-generator-EhPaB7Hn.js.map +0 -1
  46. package/dist/playlist-generator-VTkgn53O.mjs +0 -48
  47. package/dist/playlist-generator-VTkgn53O.mjs.map +0 -1
  48. package/dist/recording/index.js +0 -257
  49. package/dist/recording/index.js.map +0 -1
  50. package/dist/recording/index.mjs +0 -235
  51. package/dist/recording/index.mjs.map +0 -1
  52. package/dist/recording-coordinator-BoGr5moz.js +0 -1052
  53. package/dist/recording-coordinator-BoGr5moz.js.map +0 -1
  54. package/dist/recording-coordinator-CsYH9LqF.mjs +0 -1012
  55. package/dist/recording-coordinator-CsYH9LqF.mjs.map +0 -1
  56. package/dist/recording-db-gOgaoQh0.js +0 -348
  57. package/dist/recording-db-gOgaoQh0.js.map +0 -1
  58. package/dist/recording-db-lIkSMTLq.mjs +0 -348
  59. package/dist/recording-db-lIkSMTLq.mjs.map +0 -1
  60. package/dist/recording-service-facade-B9lG6OFn.mjs +0 -123
  61. package/dist/recording-service-facade-B9lG6OFn.mjs.map +0 -1
  62. package/dist/recording-service-facade-Do1PKlAL.js +0 -123
  63. package/dist/recording-service-facade-Do1PKlAL.js.map +0 -1
  64. package/dist/storage-estimator-CRpoQc9j.js +0 -72
  65. package/dist/storage-estimator-CRpoQc9j.js.map +0 -1
  66. package/dist/storage-estimator-DzD8gWJH.mjs +0 -72
  67. package/dist/storage-estimator-DzD8gWJH.mjs.map +0 -1
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const FALLBACK_STREAMS = ["sub", "mid", "main"];
4
- class PlaylistGenerator {
5
- constructor(db) {
6
- this.db = db;
7
- }
8
- generate(deviceId, streamId, startTime, endTime, options) {
9
- const segments = this.resolveSegments(deviceId, streamId, startTime, endTime);
10
- return this.buildPlaylist(segments, options);
11
- }
12
- resolveSegments(deviceId, streamId, startTime, endTime) {
13
- const segments = this.db.querySegments(deviceId, streamId, startTime, endTime);
14
- if (segments.length > 0) {
15
- return segments;
16
- }
17
- const fallbacks = FALLBACK_STREAMS.filter((s) => s !== streamId);
18
- for (const fb of fallbacks) {
19
- const fallbackSegments = this.db.querySegments(deviceId, fb, startTime, endTime);
20
- if (fallbackSegments.length > 0) {
21
- return fallbackSegments;
22
- }
23
- }
24
- return [];
25
- }
26
- buildPlaylist(segments, options) {
27
- const targetDuration = segments.length > 0 ? Math.ceil(Math.max(...segments.map((s) => s.duration))) : 4;
28
- const lines = [
29
- "#EXTM3U",
30
- "#EXT-X-VERSION:7",
31
- `#EXT-X-TARGETDURATION:${targetDuration}`,
32
- "#EXT-X-MEDIA-SEQUENCE:0"
33
- ];
34
- if (!options?.live) {
35
- lines.push("#EXT-X-PLAYLIST-TYPE:VOD");
36
- }
37
- for (const seg of segments) {
38
- lines.push(`#EXTINF:${seg.duration.toFixed(3)},`);
39
- lines.push(seg.path);
40
- }
41
- if (!options?.live) {
42
- lines.push("#EXT-X-ENDLIST");
43
- }
44
- return lines.join("\n");
45
- }
46
- }
47
- exports.PlaylistGenerator = PlaylistGenerator;
48
- //# sourceMappingURL=playlist-generator-EhPaB7Hn.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"playlist-generator-EhPaB7Hn.js","sources":["../src/recording/recording/playlist-generator.ts"],"sourcesContent":["import type { RecordingDb } from './recording-db.js'\nimport type { RecordingSegment } from './types.js'\n\ninterface PlaylistOptions {\n readonly live?: boolean\n}\n\nconst FALLBACK_STREAMS = ['sub', 'mid', 'main'] as const\n\nexport class PlaylistGenerator {\n constructor(private readonly db: RecordingDb) {}\n\n generate(deviceId: number, streamId: string, startTime: number, endTime: number, options?: PlaylistOptions): string {\n const segments = this.resolveSegments(deviceId, streamId, startTime, endTime)\n return this.buildPlaylist(segments, options)\n }\n\n private resolveSegments(deviceId: number, streamId: string, startTime: number, endTime: number): readonly RecordingSegment[] {\n const segments = this.db.querySegments(deviceId, streamId, startTime, endTime)\n if (segments.length > 0) {\n return segments\n }\n\n const fallbacks = FALLBACK_STREAMS.filter(s => s !== streamId)\n for (const fb of fallbacks) {\n const fallbackSegments = this.db.querySegments(deviceId, fb, startTime, endTime)\n if (fallbackSegments.length > 0) {\n return fallbackSegments\n }\n }\n\n return []\n }\n\n private buildPlaylist(segments: readonly RecordingSegment[], options?: PlaylistOptions): string {\n const targetDuration = segments.length > 0\n ? Math.ceil(Math.max(...segments.map(s => s.duration)))\n : 4\n\n const lines: string[] = [\n '#EXTM3U',\n '#EXT-X-VERSION:7',\n `#EXT-X-TARGETDURATION:${targetDuration}`,\n '#EXT-X-MEDIA-SEQUENCE:0',\n ]\n\n if (!options?.live) {\n lines.push('#EXT-X-PLAYLIST-TYPE:VOD')\n }\n\n for (const seg of segments) {\n lines.push(`#EXTINF:${seg.duration.toFixed(3)},`)\n lines.push(seg.path)\n }\n\n if (!options?.live) {\n lines.push('#EXT-X-ENDLIST')\n }\n\n return lines.join('\\n')\n }\n}\n"],"names":[],"mappings":";;AAOA,MAAM,mBAAmB,CAAC,OAAO,OAAO,MAAM;AAEvC,MAAM,kBAAkB;AAAA,EAC7B,YAA6B,IAAiB;AAAjB,SAAA,KAAA;AAAA,EAAkB;AAAA,EAE/C,SAAS,UAAkB,UAAkB,WAAmB,SAAiB,SAAmC;AAClH,UAAM,WAAW,KAAK,gBAAgB,UAAU,UAAU,WAAW,OAAO;AAC5E,WAAO,KAAK,cAAc,UAAU,OAAO;AAAA,EAC7C;AAAA,EAEQ,gBAAgB,UAAkB,UAAkB,WAAmB,SAA8C;AAC3H,UAAM,WAAW,KAAK,GAAG,cAAc,UAAU,UAAU,WAAW,OAAO;AAC7E,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,iBAAiB,OAAO,CAAA,MAAK,MAAM,QAAQ;AAC7D,eAAW,MAAM,WAAW;AAC1B,YAAM,mBAAmB,KAAK,GAAG,cAAc,UAAU,IAAI,WAAW,OAAO;AAC/E,UAAI,iBAAiB,SAAS,GAAG;AAC/B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,CAAA;AAAA,EACT;AAAA,EAEQ,cAAc,UAAuC,SAAmC;AAC9F,UAAM,iBAAiB,SAAS,SAAS,IACrC,KAAK,KAAK,KAAK,IAAI,GAAG,SAAS,IAAI,CAAA,MAAK,EAAE,QAAQ,CAAC,CAAC,IACpD;AAEJ,UAAM,QAAkB;AAAA,MACtB;AAAA,MACA;AAAA,MACA,yBAAyB,cAAc;AAAA,MACvC;AAAA,IAAA;AAGF,QAAI,CAAC,SAAS,MAAM;AAClB,YAAM,KAAK,0BAA0B;AAAA,IACvC;AAEA,eAAW,OAAO,UAAU;AAC1B,YAAM,KAAK,WAAW,IAAI,SAAS,QAAQ,CAAC,CAAC,GAAG;AAChD,YAAM,KAAK,IAAI,IAAI;AAAA,IACrB;AAEA,QAAI,CAAC,SAAS,MAAM;AAClB,YAAM,KAAK,gBAAgB;AAAA,IAC7B;AAEA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AACF;;"}
@@ -1,48 +0,0 @@
1
- const FALLBACK_STREAMS = ["sub", "mid", "main"];
2
- class PlaylistGenerator {
3
- constructor(db) {
4
- this.db = db;
5
- }
6
- generate(deviceId, streamId, startTime, endTime, options) {
7
- const segments = this.resolveSegments(deviceId, streamId, startTime, endTime);
8
- return this.buildPlaylist(segments, options);
9
- }
10
- resolveSegments(deviceId, streamId, startTime, endTime) {
11
- const segments = this.db.querySegments(deviceId, streamId, startTime, endTime);
12
- if (segments.length > 0) {
13
- return segments;
14
- }
15
- const fallbacks = FALLBACK_STREAMS.filter((s) => s !== streamId);
16
- for (const fb of fallbacks) {
17
- const fallbackSegments = this.db.querySegments(deviceId, fb, startTime, endTime);
18
- if (fallbackSegments.length > 0) {
19
- return fallbackSegments;
20
- }
21
- }
22
- return [];
23
- }
24
- buildPlaylist(segments, options) {
25
- const targetDuration = segments.length > 0 ? Math.ceil(Math.max(...segments.map((s) => s.duration))) : 4;
26
- const lines = [
27
- "#EXTM3U",
28
- "#EXT-X-VERSION:7",
29
- `#EXT-X-TARGETDURATION:${targetDuration}`,
30
- "#EXT-X-MEDIA-SEQUENCE:0"
31
- ];
32
- if (!options?.live) {
33
- lines.push("#EXT-X-PLAYLIST-TYPE:VOD");
34
- }
35
- for (const seg of segments) {
36
- lines.push(`#EXTINF:${seg.duration.toFixed(3)},`);
37
- lines.push(seg.path);
38
- }
39
- if (!options?.live) {
40
- lines.push("#EXT-X-ENDLIST");
41
- }
42
- return lines.join("\n");
43
- }
44
- }
45
- export {
46
- PlaylistGenerator
47
- };
48
- //# sourceMappingURL=playlist-generator-VTkgn53O.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"playlist-generator-VTkgn53O.mjs","sources":["../src/recording/recording/playlist-generator.ts"],"sourcesContent":["import type { RecordingDb } from './recording-db.js'\nimport type { RecordingSegment } from './types.js'\n\ninterface PlaylistOptions {\n readonly live?: boolean\n}\n\nconst FALLBACK_STREAMS = ['sub', 'mid', 'main'] as const\n\nexport class PlaylistGenerator {\n constructor(private readonly db: RecordingDb) {}\n\n generate(deviceId: number, streamId: string, startTime: number, endTime: number, options?: PlaylistOptions): string {\n const segments = this.resolveSegments(deviceId, streamId, startTime, endTime)\n return this.buildPlaylist(segments, options)\n }\n\n private resolveSegments(deviceId: number, streamId: string, startTime: number, endTime: number): readonly RecordingSegment[] {\n const segments = this.db.querySegments(deviceId, streamId, startTime, endTime)\n if (segments.length > 0) {\n return segments\n }\n\n const fallbacks = FALLBACK_STREAMS.filter(s => s !== streamId)\n for (const fb of fallbacks) {\n const fallbackSegments = this.db.querySegments(deviceId, fb, startTime, endTime)\n if (fallbackSegments.length > 0) {\n return fallbackSegments\n }\n }\n\n return []\n }\n\n private buildPlaylist(segments: readonly RecordingSegment[], options?: PlaylistOptions): string {\n const targetDuration = segments.length > 0\n ? Math.ceil(Math.max(...segments.map(s => s.duration)))\n : 4\n\n const lines: string[] = [\n '#EXTM3U',\n '#EXT-X-VERSION:7',\n `#EXT-X-TARGETDURATION:${targetDuration}`,\n '#EXT-X-MEDIA-SEQUENCE:0',\n ]\n\n if (!options?.live) {\n lines.push('#EXT-X-PLAYLIST-TYPE:VOD')\n }\n\n for (const seg of segments) {\n lines.push(`#EXTINF:${seg.duration.toFixed(3)},`)\n lines.push(seg.path)\n }\n\n if (!options?.live) {\n lines.push('#EXT-X-ENDLIST')\n }\n\n return lines.join('\\n')\n }\n}\n"],"names":[],"mappings":"AAOA,MAAM,mBAAmB,CAAC,OAAO,OAAO,MAAM;AAEvC,MAAM,kBAAkB;AAAA,EAC7B,YAA6B,IAAiB;AAAjB,SAAA,KAAA;AAAA,EAAkB;AAAA,EAE/C,SAAS,UAAkB,UAAkB,WAAmB,SAAiB,SAAmC;AAClH,UAAM,WAAW,KAAK,gBAAgB,UAAU,UAAU,WAAW,OAAO;AAC5E,WAAO,KAAK,cAAc,UAAU,OAAO;AAAA,EAC7C;AAAA,EAEQ,gBAAgB,UAAkB,UAAkB,WAAmB,SAA8C;AAC3H,UAAM,WAAW,KAAK,GAAG,cAAc,UAAU,UAAU,WAAW,OAAO;AAC7E,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,iBAAiB,OAAO,CAAA,MAAK,MAAM,QAAQ;AAC7D,eAAW,MAAM,WAAW;AAC1B,YAAM,mBAAmB,KAAK,GAAG,cAAc,UAAU,IAAI,WAAW,OAAO;AAC/E,UAAI,iBAAiB,SAAS,GAAG;AAC/B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,CAAA;AAAA,EACT;AAAA,EAEQ,cAAc,UAAuC,SAAmC;AAC9F,UAAM,iBAAiB,SAAS,SAAS,IACrC,KAAK,KAAK,KAAK,IAAI,GAAG,SAAS,IAAI,CAAA,MAAK,EAAE,QAAQ,CAAC,CAAC,IACpD;AAEJ,UAAM,QAAkB;AAAA,MACtB;AAAA,MACA;AAAA,MACA,yBAAyB,cAAc;AAAA,MACvC;AAAA,IAAA;AAGF,QAAI,CAAC,SAAS,MAAM;AAClB,YAAM,KAAK,0BAA0B;AAAA,IACvC;AAEA,eAAW,OAAO,UAAU;AAC1B,YAAM,KAAK,WAAW,IAAI,SAAS,QAAQ,CAAC,CAAC,GAAG;AAChD,YAAM,KAAK,IAAI,IAAI;AAAA,IACrB;AAEA,QAAI,CAAC,SAAS,MAAM;AAClB,YAAM,KAAK,gBAAgB;AAAA,IAC7B;AAEA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AACF;"}
@@ -1,257 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (let key of __getOwnPropNames(from))
11
- if (!__hasOwnProp.call(to, key) && key !== except)
12
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
- }
14
- return to;
15
- };
16
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
- // If the importer is in node compatibility mode or this is not an ESM
18
- // file that has been converted to a CommonJS file using a Babel-
19
- // compatible transform (i.e. "__esModule" has not been set), then set
20
- // "default" to the CommonJS "module.exports" for node compatibility.
21
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
- mod
23
- ));
24
- Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
25
- const index = require("../index-BFbwYH1P.js");
26
- class RecordingAddon extends index.BaseAddon {
27
- coordinator = null;
28
- recordingDb = null;
29
- sqliteDb = null;
30
- recordingDeps = null;
31
- serviceFacade = null;
32
- constructor() {
33
- super({
34
- ffmpegPath: "ffmpeg",
35
- hwaccel: "none",
36
- threads: 0,
37
- segmentDurationSeconds: 4,
38
- defaultRetentionDays: 30,
39
- storageWarningThreshold: 80,
40
- storageCriticalThreshold: 95,
41
- storageHighUsageThreshold: 90,
42
- retentionCheckIntervalMin: 5
43
- });
44
- }
45
- setRecordingDependencies(deps) {
46
- this.recordingDeps = deps;
47
- }
48
- async onInitialize() {
49
- if (!this.recordingDeps) {
50
- this.ctx.logger.info("Recording Engine: optional dependencies not wired — skipping init");
51
- return;
52
- }
53
- try {
54
- const Database = (await import("better-sqlite3")).default;
55
- const path = await import("node:path");
56
- const { detectPlatformDefaults } = await Promise.resolve().then(() => require("../ffmpeg-config-uANz3sV5.js"));
57
- const { RecordingDb: RecDb } = await Promise.resolve().then(() => require("../recording-db-gOgaoQh0.js"));
58
- const { RecordingCoordinator: RecCoord } = await Promise.resolve().then(() => require("../recording-coordinator-BoGr5moz.js"));
59
- const storage = this.ctx.kernel.storage;
60
- const dbPath = path.join(storage?.resolve({ location: "data", relativePath: "" }) ?? "camstack-data", "camstack.db");
61
- this.sqliteDb = new Database(dbPath);
62
- this.recordingDb = new RecDb(this.sqliteDb);
63
- this.recordingDb.initialize();
64
- const ffmpegPath = this.config.ffmpegPath;
65
- const detectedFfmpegConfig = detectPlatformDefaults(ffmpegPath);
66
- const globalFfmpegConfig = {
67
- path: ffmpegPath,
68
- hwaccel: this.config.hwaccel,
69
- threads: this.config.threads
70
- };
71
- const segmentDurationSeconds = this.config.segmentDurationSeconds;
72
- if (!storage) {
73
- throw new Error("RecordingAddon: storage capability not available");
74
- }
75
- this.coordinator = new RecCoord({
76
- db: this.recordingDb,
77
- logger: this.ctx.logger,
78
- eventBus: this.ctx.eventBus,
79
- streamingEngine: this.recordingDeps.streamingEngine,
80
- pipelineManager: this.recordingDeps.pipelineManager,
81
- networkTracker: this.recordingDeps.networkTracker,
82
- storageProvider: storage,
83
- globalFfmpegConfig,
84
- detectedFfmpegConfig,
85
- segmentDurationSec: segmentDurationSeconds
86
- });
87
- await this.coordinator.start();
88
- const { PlaylistGenerator } = await Promise.resolve().then(() => require("../playlist-generator-EhPaB7Hn.js"));
89
- const { StorageEstimator } = await Promise.resolve().then(() => require("../storage-estimator-CRpoQc9j.js"));
90
- const playlistGenerator = new PlaylistGenerator(this.recordingDb);
91
- const storageEstimator = new StorageEstimator(this.recordingDb, this.recordingDeps.networkTracker);
92
- const { RecordingServiceFacade } = await Promise.resolve().then(() => require("../recording-service-facade-Do1PKlAL.js"));
93
- this.serviceFacade = new RecordingServiceFacade({
94
- coordinator: this.coordinator,
95
- db: this.recordingDb,
96
- playlistGenerator,
97
- storageEstimator
98
- });
99
- this.ctx.logger.info("Recording Engine initialized");
100
- return [{ capability: index.recordingEngineCapability, provider: this.serviceFacade }];
101
- } catch (error) {
102
- const msg = index.errMsg(error);
103
- this.ctx.logger.warn("Recording Engine failed to initialize", { meta: { error: msg } });
104
- }
105
- }
106
- async onShutdown() {
107
- if (this.coordinator) {
108
- this.coordinator.stop();
109
- this.coordinator = null;
110
- }
111
- if (this.sqliteDb) {
112
- this.sqliteDb.close();
113
- this.sqliteDb = null;
114
- }
115
- this.recordingDb = null;
116
- }
117
- // --- Recording engine accessors ---
118
- getCoordinator() {
119
- if (!this.coordinator) throw new Error("RecordingAddon: recording not initialized");
120
- return this.coordinator;
121
- }
122
- getRecordingDb() {
123
- if (!this.recordingDb) throw new Error("RecordingAddon: recording not initialized");
124
- return this.recordingDb;
125
- }
126
- // --- Three-level settings API (Phase 3) ---
127
- //
128
- // Recording is post-detection infra. Until the dedicated post-detection
129
- // UI exists, it exposes `getGlobalSettings` so the knobs surface under
130
- // Cluster → Settings alongside other node-level infra.
131
- globalSettingsSchema() {
132
- return this.schema({
133
- sections: [
134
- {
135
- id: "recording-general",
136
- title: "Recording Settings",
137
- columns: 2,
138
- fields: [
139
- {
140
- type: "number",
141
- key: "segmentDurationSeconds",
142
- label: "Segment Duration",
143
- description: "Duration of each recording segment",
144
- min: 10,
145
- max: 3600,
146
- step: 10,
147
- default: 300,
148
- unit: "s"
149
- },
150
- {
151
- type: "number",
152
- key: "defaultRetentionDays",
153
- label: "Retention",
154
- description: "Days to keep recordings before automatic deletion",
155
- min: 1,
156
- max: 365,
157
- step: 1,
158
- default: 30,
159
- unit: "days"
160
- }
161
- ]
162
- },
163
- {
164
- id: "recording-ffmpeg",
165
- title: "FFmpeg",
166
- columns: 3,
167
- fields: [
168
- {
169
- type: "text",
170
- key: "ffmpegPath",
171
- label: "FFmpeg Path",
172
- description: "Path to ffmpeg binary",
173
- placeholder: "ffmpeg",
174
- default: "ffmpeg"
175
- },
176
- {
177
- type: "select",
178
- key: "hwaccel",
179
- label: "Hardware Acceleration",
180
- options: [
181
- { value: "none", label: "None (CPU)" },
182
- { value: "vaapi", label: "VAAPI (Intel/AMD)" },
183
- { value: "cuda", label: "CUDA (NVIDIA)" },
184
- { value: "videotoolbox", label: "VideoToolbox (macOS)" }
185
- ]
186
- },
187
- {
188
- type: "number",
189
- key: "threads",
190
- label: "Threads",
191
- description: "FFmpeg encoding threads (0 = auto)",
192
- min: 0,
193
- max: 32,
194
- step: 1,
195
- default: 0
196
- }
197
- ]
198
- },
199
- {
200
- id: "recording-retention",
201
- title: "Storage & Retention",
202
- description: "Thresholds for storage usage alerts and retention enforcement frequency.",
203
- columns: 2,
204
- fields: [
205
- {
206
- type: "number",
207
- key: "storageWarningThreshold",
208
- label: "Warning Threshold",
209
- description: "Storage usage percentage that triggers a warning event.",
210
- min: 50,
211
- max: 99,
212
- step: 1,
213
- default: 80,
214
- unit: "%"
215
- },
216
- {
217
- type: "number",
218
- key: "storageCriticalThreshold",
219
- label: "Critical Threshold",
220
- description: "Storage usage percentage that triggers a critical event.",
221
- min: 50,
222
- max: 99,
223
- step: 1,
224
- default: 95,
225
- unit: "%"
226
- },
227
- {
228
- type: "number",
229
- key: "storageHighUsageThreshold",
230
- label: "High Usage Threshold",
231
- description: "Storage usage percentage that increases retention check frequency.",
232
- min: 50,
233
- max: 99,
234
- step: 1,
235
- default: 90,
236
- unit: "%"
237
- },
238
- {
239
- type: "number",
240
- key: "retentionCheckIntervalMin",
241
- label: "Check Interval",
242
- description: "Normal interval between retention cleanup cycles.",
243
- min: 1,
244
- max: 60,
245
- step: 1,
246
- default: 5,
247
- unit: "min"
248
- }
249
- ]
250
- }
251
- ]
252
- });
253
- }
254
- }
255
- exports.RecordingAddon = RecordingAddon;
256
- exports.default = RecordingAddon;
257
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":["../../src/recording/index.ts"],"sourcesContent":["import type {\n ProviderRegistration,\n FfmpegConfig,\n IStreamingEngine,\n IPipelineManager,\n INetworkQualityTracker,\n} from '@camstack/types'\nimport { BaseAddon, recordingEngineCapability, errMsg } from '@camstack/types'\n\n// Dynamic imports to avoid hard dep on better-sqlite3 at load time\ntype RecordingCoordinator = import('./recording/recording-coordinator.js').RecordingCoordinator\ntype RecordingDb = import('./recording/recording-db.js').RecordingDb\n\nexport interface RecordingEngineDependencies {\n readonly streamingEngine: IStreamingEngine\n readonly pipelineManager: IPipelineManager\n readonly networkTracker: INetworkQualityTracker\n}\n\ninterface RecordingConfig {\n ffmpegPath: string\n hwaccel: string\n threads: number\n segmentDurationSeconds: number\n defaultRetentionDays: number\n storageWarningThreshold: number\n storageCriticalThreshold: number\n storageHighUsageThreshold: number\n retentionCheckIntervalMin: number\n}\n\nexport class RecordingAddon extends BaseAddon<RecordingConfig> {\n private coordinator: RecordingCoordinator | null = null\n private recordingDb: RecordingDb | null = null\n private sqliteDb: import('better-sqlite3').Database | null = null\n private recordingDeps: RecordingEngineDependencies | null = null\n private serviceFacade: import('./recording-service-facade.js').RecordingServiceFacade | null = null\n\n constructor() {\n super({\n ffmpegPath: 'ffmpeg',\n hwaccel: 'none',\n threads: 0,\n segmentDurationSeconds: 4,\n defaultRetentionDays: 30,\n storageWarningThreshold: 80,\n storageCriticalThreshold: 95,\n storageHighUsageThreshold: 90,\n retentionCheckIntervalMin: 5,\n })\n }\n\n setRecordingDependencies(deps: RecordingEngineDependencies): void {\n this.recordingDeps = deps\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[] | void> {\n if (!this.recordingDeps) {\n this.ctx.logger.info('Recording Engine: optional dependencies not wired — skipping init')\n return\n }\n\n try {\n const Database = (await import('better-sqlite3')).default\n const path = await import('node:path')\n const { detectPlatformDefaults } = await import('./recording/ffmpeg-config.js')\n const { RecordingDb: RecDb } = await import('./recording/recording-db.js')\n const { RecordingCoordinator: RecCoord } = await import('./recording/recording-coordinator.js')\n\n const storage = this.ctx.kernel.storage\n const dbPath = path.join(storage?.resolve({ location: 'data', relativePath: '' }) ?? 'camstack-data', 'camstack.db')\n this.sqliteDb = new Database(dbPath)\n this.recordingDb = new RecDb(this.sqliteDb)\n this.recordingDb.initialize()\n\n const ffmpegPath = this.config.ffmpegPath\n const detectedFfmpegConfig = detectPlatformDefaults(ffmpegPath)\n\n const globalFfmpegConfig: Partial<FfmpegConfig> = {\n path: ffmpegPath,\n hwaccel: this.config.hwaccel as FfmpegConfig['hwaccel'],\n threads: this.config.threads,\n }\n\n const segmentDurationSeconds = this.config.segmentDurationSeconds\n\n if (!storage) {\n throw new Error('RecordingAddon: storage capability not available')\n }\n\n this.coordinator = new RecCoord({\n db: this.recordingDb,\n logger: this.ctx.logger,\n eventBus: this.ctx.eventBus,\n streamingEngine: this.recordingDeps.streamingEngine,\n pipelineManager: this.recordingDeps.pipelineManager,\n networkTracker: this.recordingDeps.networkTracker,\n storageProvider: storage,\n globalFfmpegConfig,\n detectedFfmpegConfig,\n segmentDurationSec: segmentDurationSeconds,\n })\n await this.coordinator.start()\n\n // Create service facade wrapping all internal services.\n // The facade is the capability provider — the auto-generated cap router\n // dispatches typed tRPC procedures directly to facade methods.\n const { PlaylistGenerator } = await import('./recording/playlist-generator.js')\n const { StorageEstimator } = await import('./recording/storage-estimator.js')\n const playlistGenerator = new PlaylistGenerator(this.recordingDb)\n const storageEstimator = new StorageEstimator(this.recordingDb, this.recordingDeps.networkTracker)\n\n const { RecordingServiceFacade } = await import('./recording-service-facade.js')\n this.serviceFacade = new RecordingServiceFacade({\n coordinator: this.coordinator,\n db: this.recordingDb,\n playlistGenerator,\n storageEstimator,\n })\n this.ctx.logger.info('Recording Engine initialized')\n return [{ capability: recordingEngineCapability, provider: this.serviceFacade }]\n } catch (error: unknown) {\n const msg = errMsg(error)\n this.ctx.logger.warn('Recording Engine failed to initialize', { meta: { error: msg } })\n }\n }\n\n protected async onShutdown(): Promise<void> {\n if (this.coordinator) {\n this.coordinator.stop()\n this.coordinator = null\n }\n if (this.sqliteDb) {\n this.sqliteDb.close()\n this.sqliteDb = null\n }\n this.recordingDb = null\n }\n\n // --- Recording engine accessors ---\n\n getCoordinator(): RecordingCoordinator {\n if (!this.coordinator) throw new Error('RecordingAddon: recording not initialized')\n return this.coordinator\n }\n\n getRecordingDb(): RecordingDb {\n if (!this.recordingDb) throw new Error('RecordingAddon: recording not initialized')\n return this.recordingDb\n }\n\n // --- Three-level settings API (Phase 3) ---\n //\n // Recording is post-detection infra. Until the dedicated post-detection\n // UI exists, it exposes `getGlobalSettings` so the knobs surface under\n // Cluster → Settings alongside other node-level infra.\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'recording-general',\n title: 'Recording Settings',\n columns: 2,\n fields: [\n {\n type: 'number',\n key: 'segmentDurationSeconds',\n label: 'Segment Duration',\n description: 'Duration of each recording segment',\n min: 10,\n max: 3600,\n step: 10,\n default: 300,\n unit: 's',\n },\n {\n type: 'number',\n key: 'defaultRetentionDays',\n label: 'Retention',\n description: 'Days to keep recordings before automatic deletion',\n min: 1,\n max: 365,\n step: 1,\n default: 30,\n unit: 'days',\n },\n ],\n },\n {\n id: 'recording-ffmpeg',\n title: 'FFmpeg',\n columns: 3,\n fields: [\n {\n type: 'text',\n key: 'ffmpegPath',\n label: 'FFmpeg Path',\n description: 'Path to ffmpeg binary',\n placeholder: 'ffmpeg',\n default: 'ffmpeg',\n },\n {\n type: 'select',\n key: 'hwaccel',\n label: 'Hardware Acceleration',\n options: [\n { value: 'none', label: 'None (CPU)' },\n { value: 'vaapi', label: 'VAAPI (Intel/AMD)' },\n { value: 'cuda', label: 'CUDA (NVIDIA)' },\n { value: 'videotoolbox', label: 'VideoToolbox (macOS)' },\n ],\n },\n {\n type: 'number',\n key: 'threads',\n label: 'Threads',\n description: 'FFmpeg encoding threads (0 = auto)',\n min: 0,\n max: 32,\n step: 1,\n default: 0,\n },\n ],\n },\n {\n id: 'recording-retention',\n title: 'Storage & Retention',\n description: 'Thresholds for storage usage alerts and retention enforcement frequency.',\n columns: 2,\n fields: [\n {\n type: 'number',\n key: 'storageWarningThreshold',\n label: 'Warning Threshold',\n description: 'Storage usage percentage that triggers a warning event.',\n min: 50, max: 99, step: 1, default: 80, unit: '%',\n },\n {\n type: 'number',\n key: 'storageCriticalThreshold',\n label: 'Critical Threshold',\n description: 'Storage usage percentage that triggers a critical event.',\n min: 50, max: 99, step: 1, default: 95, unit: '%',\n },\n {\n type: 'number',\n key: 'storageHighUsageThreshold',\n label: 'High Usage Threshold',\n description: 'Storage usage percentage that increases retention check frequency.',\n min: 50, max: 99, step: 1, default: 90, unit: '%',\n },\n {\n type: 'number',\n key: 'retentionCheckIntervalMin',\n label: 'Check Interval',\n description: 'Normal interval between retention cleanup cycles.',\n min: 1, max: 60, step: 1, default: 5, unit: 'min',\n },\n ],\n },\n ],\n })\n }\n\n}\n\nexport default RecordingAddon\n"],"names":["BaseAddon","recordingEngineCapability","errMsg"],"mappings":"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"}
@@ -1,235 +0,0 @@
1
- import { B as BaseAddon, r as recordingEngineCapability, e as errMsg } from "../index-BrTlzsrE.mjs";
2
- class RecordingAddon extends BaseAddon {
3
- coordinator = null;
4
- recordingDb = null;
5
- sqliteDb = null;
6
- recordingDeps = null;
7
- serviceFacade = null;
8
- constructor() {
9
- super({
10
- ffmpegPath: "ffmpeg",
11
- hwaccel: "none",
12
- threads: 0,
13
- segmentDurationSeconds: 4,
14
- defaultRetentionDays: 30,
15
- storageWarningThreshold: 80,
16
- storageCriticalThreshold: 95,
17
- storageHighUsageThreshold: 90,
18
- retentionCheckIntervalMin: 5
19
- });
20
- }
21
- setRecordingDependencies(deps) {
22
- this.recordingDeps = deps;
23
- }
24
- async onInitialize() {
25
- if (!this.recordingDeps) {
26
- this.ctx.logger.info("Recording Engine: optional dependencies not wired — skipping init");
27
- return;
28
- }
29
- try {
30
- const Database = (await import("better-sqlite3")).default;
31
- const path = await import("node:path");
32
- const { detectPlatformDefaults } = await import("../ffmpeg-config-DRONlBsj.mjs");
33
- const { RecordingDb: RecDb } = await import("../recording-db-lIkSMTLq.mjs");
34
- const { RecordingCoordinator: RecCoord } = await import("../recording-coordinator-CsYH9LqF.mjs");
35
- const storage = this.ctx.kernel.storage;
36
- const dbPath = path.join(storage?.resolve({ location: "data", relativePath: "" }) ?? "camstack-data", "camstack.db");
37
- this.sqliteDb = new Database(dbPath);
38
- this.recordingDb = new RecDb(this.sqliteDb);
39
- this.recordingDb.initialize();
40
- const ffmpegPath = this.config.ffmpegPath;
41
- const detectedFfmpegConfig = detectPlatformDefaults(ffmpegPath);
42
- const globalFfmpegConfig = {
43
- path: ffmpegPath,
44
- hwaccel: this.config.hwaccel,
45
- threads: this.config.threads
46
- };
47
- const segmentDurationSeconds = this.config.segmentDurationSeconds;
48
- if (!storage) {
49
- throw new Error("RecordingAddon: storage capability not available");
50
- }
51
- this.coordinator = new RecCoord({
52
- db: this.recordingDb,
53
- logger: this.ctx.logger,
54
- eventBus: this.ctx.eventBus,
55
- streamingEngine: this.recordingDeps.streamingEngine,
56
- pipelineManager: this.recordingDeps.pipelineManager,
57
- networkTracker: this.recordingDeps.networkTracker,
58
- storageProvider: storage,
59
- globalFfmpegConfig,
60
- detectedFfmpegConfig,
61
- segmentDurationSec: segmentDurationSeconds
62
- });
63
- await this.coordinator.start();
64
- const { PlaylistGenerator } = await import("../playlist-generator-VTkgn53O.mjs");
65
- const { StorageEstimator } = await import("../storage-estimator-DzD8gWJH.mjs");
66
- const playlistGenerator = new PlaylistGenerator(this.recordingDb);
67
- const storageEstimator = new StorageEstimator(this.recordingDb, this.recordingDeps.networkTracker);
68
- const { RecordingServiceFacade } = await import("../recording-service-facade-B9lG6OFn.mjs");
69
- this.serviceFacade = new RecordingServiceFacade({
70
- coordinator: this.coordinator,
71
- db: this.recordingDb,
72
- playlistGenerator,
73
- storageEstimator
74
- });
75
- this.ctx.logger.info("Recording Engine initialized");
76
- return [{ capability: recordingEngineCapability, provider: this.serviceFacade }];
77
- } catch (error) {
78
- const msg = errMsg(error);
79
- this.ctx.logger.warn("Recording Engine failed to initialize", { meta: { error: msg } });
80
- }
81
- }
82
- async onShutdown() {
83
- if (this.coordinator) {
84
- this.coordinator.stop();
85
- this.coordinator = null;
86
- }
87
- if (this.sqliteDb) {
88
- this.sqliteDb.close();
89
- this.sqliteDb = null;
90
- }
91
- this.recordingDb = null;
92
- }
93
- // --- Recording engine accessors ---
94
- getCoordinator() {
95
- if (!this.coordinator) throw new Error("RecordingAddon: recording not initialized");
96
- return this.coordinator;
97
- }
98
- getRecordingDb() {
99
- if (!this.recordingDb) throw new Error("RecordingAddon: recording not initialized");
100
- return this.recordingDb;
101
- }
102
- // --- Three-level settings API (Phase 3) ---
103
- //
104
- // Recording is post-detection infra. Until the dedicated post-detection
105
- // UI exists, it exposes `getGlobalSettings` so the knobs surface under
106
- // Cluster → Settings alongside other node-level infra.
107
- globalSettingsSchema() {
108
- return this.schema({
109
- sections: [
110
- {
111
- id: "recording-general",
112
- title: "Recording Settings",
113
- columns: 2,
114
- fields: [
115
- {
116
- type: "number",
117
- key: "segmentDurationSeconds",
118
- label: "Segment Duration",
119
- description: "Duration of each recording segment",
120
- min: 10,
121
- max: 3600,
122
- step: 10,
123
- default: 300,
124
- unit: "s"
125
- },
126
- {
127
- type: "number",
128
- key: "defaultRetentionDays",
129
- label: "Retention",
130
- description: "Days to keep recordings before automatic deletion",
131
- min: 1,
132
- max: 365,
133
- step: 1,
134
- default: 30,
135
- unit: "days"
136
- }
137
- ]
138
- },
139
- {
140
- id: "recording-ffmpeg",
141
- title: "FFmpeg",
142
- columns: 3,
143
- fields: [
144
- {
145
- type: "text",
146
- key: "ffmpegPath",
147
- label: "FFmpeg Path",
148
- description: "Path to ffmpeg binary",
149
- placeholder: "ffmpeg",
150
- default: "ffmpeg"
151
- },
152
- {
153
- type: "select",
154
- key: "hwaccel",
155
- label: "Hardware Acceleration",
156
- options: [
157
- { value: "none", label: "None (CPU)" },
158
- { value: "vaapi", label: "VAAPI (Intel/AMD)" },
159
- { value: "cuda", label: "CUDA (NVIDIA)" },
160
- { value: "videotoolbox", label: "VideoToolbox (macOS)" }
161
- ]
162
- },
163
- {
164
- type: "number",
165
- key: "threads",
166
- label: "Threads",
167
- description: "FFmpeg encoding threads (0 = auto)",
168
- min: 0,
169
- max: 32,
170
- step: 1,
171
- default: 0
172
- }
173
- ]
174
- },
175
- {
176
- id: "recording-retention",
177
- title: "Storage & Retention",
178
- description: "Thresholds for storage usage alerts and retention enforcement frequency.",
179
- columns: 2,
180
- fields: [
181
- {
182
- type: "number",
183
- key: "storageWarningThreshold",
184
- label: "Warning Threshold",
185
- description: "Storage usage percentage that triggers a warning event.",
186
- min: 50,
187
- max: 99,
188
- step: 1,
189
- default: 80,
190
- unit: "%"
191
- },
192
- {
193
- type: "number",
194
- key: "storageCriticalThreshold",
195
- label: "Critical Threshold",
196
- description: "Storage usage percentage that triggers a critical event.",
197
- min: 50,
198
- max: 99,
199
- step: 1,
200
- default: 95,
201
- unit: "%"
202
- },
203
- {
204
- type: "number",
205
- key: "storageHighUsageThreshold",
206
- label: "High Usage Threshold",
207
- description: "Storage usage percentage that increases retention check frequency.",
208
- min: 50,
209
- max: 99,
210
- step: 1,
211
- default: 90,
212
- unit: "%"
213
- },
214
- {
215
- type: "number",
216
- key: "retentionCheckIntervalMin",
217
- label: "Check Interval",
218
- description: "Normal interval between retention cleanup cycles.",
219
- min: 1,
220
- max: 60,
221
- step: 1,
222
- default: 5,
223
- unit: "min"
224
- }
225
- ]
226
- }
227
- ]
228
- });
229
- }
230
- }
231
- export {
232
- RecordingAddon,
233
- RecordingAddon as default
234
- };
235
- //# sourceMappingURL=index.mjs.map