@memberjunction/livekit-room-server 5.43.0 → 5.45.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.
package/README.md CHANGED
@@ -76,16 +76,50 @@ import { LiveKitEgressService } from '@memberjunction/livekit-room-server';
76
76
 
77
77
  const egress = new LiveKitEgressService();
78
78
  const rec = await egress.StartRoomRecording({ RoomName: 'support-42' }); // → { EgressID, Status }
79
- await egress.StopRecording(rec.EgressID);
79
+ const done = await egress.StopRecording(rec.EgressID);
80
+ // On stop/complete, RecordingInfo also surfaces the produced file:
81
+ // done.OutputLocation – the file's path/key in the egress sink
82
+ // done.OutputSizeBytes – byte count
83
+ // done.OutputDurationMs – duration in ms (normalized from the SDK's nanoseconds)
84
+ // These are what the server registers into MJStorage on the Meeting-Room Conversation
85
+ // (Conversation.RecordingFileID + EgressID). While recording is in progress they are undefined.
80
86
  ```
81
87
 
82
88
  Egress output storage (S3 / GCS / Azure / LiveKit Cloud) is configured on the LiveKit project.
83
89
 
90
+ ## Meeting-recording registration (egress → MJStorage → Conversation)
91
+
92
+ When the browser stops a room recording, MJServer's `RealtimeBridgeResolver.StopLiveKitRecording` calls
93
+ `registerMeetingRecordingFile` (`packages/MJServer/src/resolvers/meetingRecordingRegistration.ts`), which:
94
+
95
+ 1. Resolves the room's **Meeting-Room Conversation** (by `EgressID`, then room name, else creates one).
96
+ 2. Creates an **`MJ: Files`** row for the egress MP4 (`ProviderKey` = `OutputLocation`, `ContentType =
97
+ video/mp4`, `Status = Uploaded`). **v1 points the Files row directly at the egress output — no byte
98
+ copy** — so playback streams straight from the sink.
99
+ 3. Stamps **`Conversation.RecordingFileID`** (+ `EgressID`) and returns the new file id on the result.
100
+
101
+ This requires the egress sink and the MJStorage account to target the **same** bucket/container, configured
102
+ via the provider:
103
+
104
+ - `MJ_MEETING_RECORDING_STORAGE_PROVIDER=<MJ: File Storage Providers ID>` (or
105
+ `meetingRecordingStorageProviderID` in `mj.config.cjs`).
106
+
107
+ Optional **copy-to-canonical** ("copy into Box") — set a *different* canonical provider via
108
+ `MJ_MEETING_RECORDING_CANONICAL_STORAGE_PROVIDER` (or `meetingRecordingCanonicalStorageProviderID`) to read
109
+ the bytes out of the sink and re-upload them into a separate provider, pointing the Files row there. OFF by
110
+ default.
111
+
112
+ Registration is **best-effort**: a missing storage provider or any failure leaves `RecordingFileID` unset
113
+ and logs — the stop-recording mutation still succeeds. See
114
+ [REALTIME_SESSION_CAPTURE_GUIDE.md](../../guides/REALTIME_SESSION_CAPTURE_GUIDE.md#meeting-room-recording-livekit-egress--mjstorage)
115
+ for the full loop incl. Meet-app playback.
116
+
84
117
  ## GraphQL surface
85
118
 
86
119
  These are exposed to the browser via MJServer's `RealtimeBridgeResolver`:
87
- `MintLiveKitClientToken`, `StartLiveKitAgentRoomSession`, `StartLiveKitRecording`, `StopLiveKitRecording`.
88
- Call them from the browser with `GraphQLLiveKitClient` in `@memberjunction/graphql-dataprovider`.
120
+ `MintLiveKitClientToken`, `StartLiveKitAgentRoomSession`, `StartLiveKitRecording`, `StopLiveKitRecording`
121
+ (returns `RecordingFileID` once registered). Call them from the browser with `GraphQLLiveKitClient` in
122
+ `@memberjunction/graphql-dataprovider`.
89
123
 
90
124
  ## License
91
125
 
@@ -32,6 +32,16 @@ export interface RecordingInfo {
32
32
  RoomName: string;
33
33
  /** The raw egress status string. */
34
34
  Status: string;
35
+ /**
36
+ * The output file's path/key in the egress storage sink, once the egress has produced a result
37
+ * (populated on stop/complete). This is what a caller copies into MJStorage + records as the
38
+ * Conversation's recording. `undefined` while the recording is still in progress.
39
+ */
40
+ OutputLocation?: string;
41
+ /** The output file size in bytes, when the egress result reports it. */
42
+ OutputSizeBytes?: number;
43
+ /** The recording duration in milliseconds, when the egress result reports it. */
44
+ OutputDurationMs?: number;
35
45
  }
36
46
  /** Starts/stops/lists LiveKit room recordings (composite egress). */
37
47
  export declare class LiveKitEgressService {
@@ -1 +1 @@
1
- {"version":3,"file":"livekit-egress-service.d.ts","sourceRoot":"","sources":["../src/livekit-egress-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAuD,MAAM,oBAAoB,CAAC;AACvG,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAExF,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mGAAmG;AACnG,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,EAAE,0BAA0B,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC;AAE5G,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qEAAqE;AACrE,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAE1C;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,EAAE,gBAAgB;IAM5E;;;;;OAKG;IACU,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAOrF;;;;;OAKG;IACU,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKpE;;;;;OAKG;IACU,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAK7E,wEAAwE;IACxE,OAAO,CAAC,eAAe;CAGxB"}
1
+ {"version":3,"file":"livekit-egress-service.d.ts","sourceRoot":"","sources":["../src/livekit-egress-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAuD,MAAM,oBAAoB,CAAC;AACvG,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAExF,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mGAAmG;AACnG,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,EAAE,0BAA0B,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC;AAE5G,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qEAAqE;AACrE,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAE1C;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,EAAE,gBAAgB;IAM5E;;;;;OAKG;IACU,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IAOrF;;;;;OAKG;IACU,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKpE;;;;;OAKG;IACU,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAK7E,wEAAwE;IACxE,OAAO,CAAC,eAAe;CAuBxB"}
@@ -62,7 +62,27 @@ export class LiveKitEgressService {
62
62
  }
63
63
  /** Maps an SDK `EgressInfo` to the normalized {@link RecordingInfo}. */
64
64
  toRecordingInfo(info, roomName) {
65
- return { EgressID: info.egressId, RoomName: info.roomName || roomName, Status: String(info.status ?? 'unknown') };
65
+ const result = {
66
+ EgressID: info.egressId,
67
+ RoomName: info.roomName || roomName,
68
+ Status: String(info.status ?? 'unknown'),
69
+ };
70
+ // Once the egress has produced its file (on stop/complete), surface the output path + size +
71
+ // duration so a caller can copy the MP4 into MJStorage and record it on the Conversation.
72
+ const fileResult = info.fileResults?.[0];
73
+ if (fileResult) {
74
+ if (fileResult.filename) {
75
+ result.OutputLocation = fileResult.filename;
76
+ }
77
+ if (fileResult.size != null) {
78
+ result.OutputSizeBytes = Number(fileResult.size);
79
+ }
80
+ if (fileResult.duration != null) {
81
+ // EncodedFileResult.duration is reported in nanoseconds — normalize to milliseconds.
82
+ result.OutputDurationMs = Math.round(Number(fileResult.duration) / 1_000_000);
83
+ }
84
+ }
85
+ return result;
66
86
  }
67
87
  }
68
88
  //# sourceMappingURL=livekit-egress-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"livekit-egress-service.js","sourceRoot":"","sources":["../src/livekit-egress-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAmB,MAAM,oBAAoB,CAAC;AACvG,OAAO,EAAE,mBAAmB,EAA4B,MAAM,yBAAyB,CAAC;AAexF,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC;AAYD,qEAAqE;AACrE,MAAM,OAAO,oBAAoB;IAG/B;;;;;OAKG;IACH,YAAY,MAAqC,EAAE,MAAyB;QAC1E,MAAM,KAAK,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,IAAI,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACxJ,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,kBAAkB,CAAC,MAA4B;QAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;QAC3E,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,QAAQ,EAAE,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,wBAAwB,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,cAAc,EAAE,CAAC,CAAC;QACxI,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,aAAa,CAAC,QAAgB;QACzC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,oBAAoB,CAAC,QAAgB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,wEAAwE;IAChE,eAAe,CAAC,IAAgB,EAAE,QAAgB;QACxD,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,EAAE,CAAC;IACpH,CAAC;CACF"}
1
+ {"version":3,"file":"livekit-egress-service.js","sourceRoot":"","sources":["../src/livekit-egress-service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/livekit-room-server",
3
3
  "type": "module",
4
- "version": "5.43.0",
4
+ "version": "5.45.0",
5
5
  "description": "MemberJunction: Server-side LiveKit room support — mints scoped client/bot access tokens (livekit-server-sdk) and provides the session-start coordinator that opens a realtime model session and bridges it into a LiveKit room via AIBridgeEngine. The MJ realtime-bridge ↔ LiveKit UI seam.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -16,13 +16,13 @@
16
16
  "author": "MemberJunction.com",
17
17
  "license": "ISC",
18
18
  "dependencies": {
19
- "@memberjunction/core": "5.43.0",
20
- "@memberjunction/global": "5.43.0",
21
- "@memberjunction/core-entities": "5.43.0",
22
- "@memberjunction/ai": "5.43.0",
23
- "@memberjunction/ai-bridge-base": "5.43.0",
24
- "@memberjunction/ai-bridge-server": "5.43.0",
25
- "@memberjunction/ai-bridge-livekit": "5.43.0",
19
+ "@memberjunction/core": "5.45.0",
20
+ "@memberjunction/global": "5.45.0",
21
+ "@memberjunction/core-entities": "5.45.0",
22
+ "@memberjunction/ai": "5.45.0",
23
+ "@memberjunction/ai-bridge-base": "5.45.0",
24
+ "@memberjunction/ai-bridge-server": "5.45.0",
25
+ "@memberjunction/ai-bridge-livekit": "5.45.0",
26
26
  "livekit-server-sdk": "^2.9.7"
27
27
  },
28
28
  "devDependencies": {