fcr-core 3.10.2 → 3.11.0-rc.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.
Files changed (37) hide show
  1. package/lib/imports.d.ts +7 -1
  2. package/lib/imports.js +27 -1
  3. package/lib/index.d.ts +2 -0
  4. package/lib/index.js +14 -2
  5. package/lib/room-control/helpers/constants.d.ts +8 -0
  6. package/lib/room-control/helpers/constants.js +8 -0
  7. package/lib/room-control/index.js +145 -1
  8. package/lib/room-control/infinity-room-control/index.js +3 -4
  9. package/lib/room-control/mainroom-control/index.js +3 -4
  10. package/lib/room-control/privilege-control/index.js +22 -3
  11. package/lib/room-control/privilege-control/type.d.ts +20 -0
  12. package/lib/room-control/privilege-control/type.js +11 -1
  13. package/lib/room-control/type.d.ts +44 -0
  14. package/lib/room-control/user-control/index.js +32 -0
  15. package/lib/room-control/user-control/type.d.ts +24 -0
  16. package/lib/room-control/whiteboard-control-v2/base/index.js +9 -12
  17. package/lib/room-control/whiteboard-control-v2/base/main-window.d.ts +6 -0
  18. package/lib/room-control/whiteboard-control-v2/base/main-window.js +13 -3
  19. package/lib/room-control/whiteboard-control-v2/type.d.ts +5 -0
  20. package/lib/service/api.d.ts +15 -1
  21. package/lib/service/api.js +48 -0
  22. package/lib/utilities/parameters.js +17 -4
  23. package/lib-es/imports.js +6 -0
  24. package/lib-es/index.js +4 -0
  25. package/lib-es/media-control/desktop.js +1 -0
  26. package/lib-es/room-control/helpers/constants.js +8 -0
  27. package/lib-es/room-control/index.js +146 -2
  28. package/lib-es/room-control/infinity-room-control/index.js +3 -4
  29. package/lib-es/room-control/mainroom-control/index.js +3 -4
  30. package/lib-es/room-control/privilege-control/index.js +23 -4
  31. package/lib-es/room-control/privilege-control/type.js +10 -0
  32. package/lib-es/room-control/user-control/index.js +32 -0
  33. package/lib-es/room-control/whiteboard-control-v2/base/index.js +9 -12
  34. package/lib-es/room-control/whiteboard-control-v2/base/main-window.js +13 -3
  35. package/lib-es/service/api.js +48 -0
  36. package/lib-es/utilities/parameters.js +17 -4
  37. package/package.json +10 -8
package/lib/imports.d.ts CHANGED
@@ -27,7 +27,13 @@ export { sleep, jsonstring, randomString } from 'agora-foundation/lib/utilities/
27
27
  export { z, ZodError, anySchema, booleanSchema, numberSchema, stringSchema, fcrRenderViewSchema, unknownSchema, objectSchema, createRecordSchemaWithKey, createUnionSchema, createArraySchema, } from 'agora-foundation/lib/utilities/schema';
28
28
  export { validateParams } from 'agora-foundation/lib/decorator/validate';
29
29
  export { Mutex } from 'agora-foundation/lib/worker/mutex';
30
- export type { AgoraRtcNetworkStats, AgoraRtcDeviceInfo, AgoraRtcDisplayInfo, AgoraRtcWindowInfo, AgoraRtePerformanceInfo, AgoraRteScenePropertiesDeletedEvent, AgoraRteStreamEncryptionConfig, AgoraRteStreamJoinConfig, AgoraRteDualVideoStreamConfig, AgoraRteMessage, AgoraRteScenePropertiesUpdatedEvent, AgoraRteRenderView, AgoraRteScene, } from 'agora-rte-sdk';
30
+ export type { AgoraRtcNetworkStats, AgoraRtcDeviceInfo, AgoraRtcDisplayInfo, AgoraRtcWindowInfo, AgoraRtePerformanceInfo, AgoraRteScenePropertiesDeletedEvent, AgoraRteStreamEncryptionConfig, AgoraRteStreamJoinConfig, AgoraRteDualVideoStreamConfig, AgoraRteMessage, AgoraRteScenePropertiesUpdatedEvent, AgoraRteRenderView, AgoraRteScene, AgoraRteClientRecordingConfig, AgoraRteClientRecordingObserver, AgoraRteRecorderInfo, } from 'agora-rte-sdk';
31
+ export { AgoraRteRecorderState, AgoraRteRecorderReasonCode } from 'agora-rte-sdk';
32
+ export type { AgoraRteClientRecordingConfig as FcrClientRecordingConfig } from 'agora-rte-sdk';
33
+ export type { AgoraRteClientRecordingObserver as FcrClientRecordingObserver } from 'agora-rte-sdk';
34
+ export type { AgoraRteRecorderInfo as FcrRecorderInfo } from 'agora-rte-sdk';
35
+ export { AgoraRteRecorderState as FcrRecorderState } from 'agora-rte-sdk';
36
+ export { AgoraRteRecorderReasonCode as FcrRecorderReasonCode } from 'agora-rte-sdk';
31
37
  export type { AgoraRtmClient } from 'agora-rte-sdk/lib/core/rtm/client';
32
38
  export type { AgoraRteEngineObserver } from 'agora-rte-sdk/lib/core/engine/type';
33
39
  export type { AgoraRteNetworkQualityEvent } from 'agora-rte-sdk/lib/core/scene/type';
package/lib/imports.js CHANGED
@@ -150,6 +150,18 @@ Object.defineProperty(exports, "AgoraRteMediaStreamType", {
150
150
  return _type.AgoraRteMediaStreamType;
151
151
  }
152
152
  });
153
+ Object.defineProperty(exports, "AgoraRteRecorderReasonCode", {
154
+ enumerable: true,
155
+ get: function () {
156
+ return _agoraRteSdk.AgoraRteRecorderReasonCode;
157
+ }
158
+ });
159
+ Object.defineProperty(exports, "AgoraRteRecorderState", {
160
+ enumerable: true,
161
+ get: function () {
162
+ return _agoraRteSdk.AgoraRteRecorderState;
163
+ }
164
+ });
153
165
  Object.defineProperty(exports, "AgoraRteRegion", {
154
166
  enumerable: true,
155
167
  get: function () {
@@ -294,6 +306,18 @@ Object.defineProperty(exports, "FcrRTMProvider_2_2", {
294
306
  return _forgeRtm.RTMProvider_2_2;
295
307
  }
296
308
  });
309
+ Object.defineProperty(exports, "FcrRecorderReasonCode", {
310
+ enumerable: true,
311
+ get: function () {
312
+ return _agoraRteSdk.AgoraRteRecorderReasonCode;
313
+ }
314
+ });
315
+ Object.defineProperty(exports, "FcrRecorderState", {
316
+ enumerable: true,
317
+ get: function () {
318
+ return _agoraRteSdk.AgoraRteRecorderState;
319
+ }
320
+ });
297
321
  Object.defineProperty(exports, "FcrRoom", {
298
322
  enumerable: true,
299
323
  get: function () {
@@ -587,9 +611,9 @@ var _misc = require("agora-foundation/lib/utilities/misc");
587
611
  var _schema = require("agora-foundation/lib/utilities/schema");
588
612
  var _validate = require("agora-foundation/lib/decorator/validate");
589
613
  var _mutex = require("agora-foundation/lib/worker/mutex");
614
+ var _agoraRteSdk = require("agora-rte-sdk");
590
615
  var _error = require("agora-rte-sdk/lib/core/utilities/error");
591
616
  var _agoraError = require("agora-foundation/lib/utilities/error/agora-error");
592
- var _agoraRteSdk = require("agora-rte-sdk");
593
617
  var _type = require("agora-rte-sdk/lib/core/scene/type");
594
618
  var _type2 = require("agora-rte-sdk/lib/core/rtc/type");
595
619
  var _type3 = require("agora-rte-sdk/lib/type");
@@ -619,4 +643,6 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
619
643
 
620
644
  // Agora RTE exports
621
645
 
646
+ // Fcr-prefixed re-exports for edu-core layer
647
+
622
648
  const localStorage = exports.localStorage = window.localStorage;
package/lib/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export type { FcrDesktopMediaControl, FcrDesktopMediaObserver, FcrMobileMediaControl, FcrMobileMediaObserver, FcrMonitorControl, FcrMonitorObserver, FcrBaseRoomControl, FcrRoomObserver, FcrUserControl, FcrUserObserver, FcrStreamControl, FcrStreamObserver, FcrPrivilegeControl, FcrPermission, FcrBoardMainWindow, FcrBoardMainWindowObserver, FcrColor, } from './type';
2
+ export type { FcrClientRecordingConfig } from './imports';
3
+ export { FcrRecorderState, FcrRecorderReasonCode } from './imports';
2
4
  export { FcrStreamLatencyLevel, FcrPermissionAction, FcrPrivilegeUserRole, FcrUserRole, FcrStreamState, FcrRegion, FcrStreamType, FcrUserUpdatedReason, FcrRoomConnectorType, FcrVideoSourceType, FcrAudioSourceType, FcrMediaSourceState, FcrVideoRenderMode, FcrVideoStreamType, FcrCapability, FcrAiDenoiseLevel, FcrStreamPrivilegeOperation, FcrStreamPrivilegeVideoSourceType, FcrStreamPrivilegeAudioSourceType, FcrConnectionState, registerPlugin, FcrBoardToolType, FcrBoardShape, FcrVideoEncoderConfig, FcrDualVideoStreamConfig, } from './type';
3
5
  export { FcrCoreEngineImpl as FcrCoreEngine } from './engine';
4
6
  export { FcrCoreEngineConfig } from './struct';
package/lib/index.js CHANGED
@@ -87,6 +87,18 @@ Object.defineProperty(exports, "FcrPrivilegeUserRole", {
87
87
  return _type.FcrPrivilegeUserRole;
88
88
  }
89
89
  });
90
+ Object.defineProperty(exports, "FcrRecorderReasonCode", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _imports.FcrRecorderReasonCode;
94
+ }
95
+ });
96
+ Object.defineProperty(exports, "FcrRecorderState", {
97
+ enumerable: true,
98
+ get: function () {
99
+ return _imports.FcrRecorderState;
100
+ }
101
+ });
90
102
  Object.defineProperty(exports, "FcrRegion", {
91
103
  enumerable: true,
92
104
  get: function () {
@@ -183,8 +195,8 @@ Object.defineProperty(exports, "registerPlugin", {
183
195
  return _type.registerPlugin;
184
196
  }
185
197
  });
198
+ var _imports = require("./imports");
186
199
  var _type = require("./type");
187
200
  var _engine = require("./engine");
188
201
  var _struct = require("./struct");
189
- var _schema = require("./schema");
190
- var _imports = require("./imports");
202
+ var _schema = require("./schema");
@@ -44,6 +44,14 @@ export declare const ROOM_MESSAGE_COMMANDS: {
44
44
  readonly LIVE_STREAMING_UPDATE: 700;
45
45
  /** 用户被踢出命令 */
46
46
  readonly USER_KICK_OUT: 5;
47
+ /** 用户widget属性更新命令(widget属性变更的通用cause.cmd) */
48
+ readonly USER_WIDGET_PROPERTIES_UPDATE: 10;
49
+ /** 客户端录制权限变更(角色级, cause.cmd=3260) */
50
+ readonly CLIENT_RECORDING_PERMISSION: 3260;
51
+ /** 客户端录制状态变更 cause.data.widgetCause.cmd=3261 (通过widget属性下发) */
52
+ readonly CLIENT_RECORDING_STATE: 3261;
53
+ /** 用户权限变更(用户级,含客户端录制) */
54
+ readonly USER_PERMISSION_UPDATE: 3121;
47
55
  /** 房间会话请求命令 */
48
56
  readonly ROOM_SESSION_REQUEST: 1001;
49
57
  /** 房间会话接受命令 */
@@ -53,6 +53,14 @@ const ROOM_MESSAGE_COMMANDS = exports.ROOM_MESSAGE_COMMANDS = {
53
53
  LIVE_STREAMING_UPDATE: 700,
54
54
  /** 用户被踢出命令 */
55
55
  USER_KICK_OUT: 5,
56
+ /** 用户widget属性更新命令(widget属性变更的通用cause.cmd) */
57
+ USER_WIDGET_PROPERTIES_UPDATE: 10,
58
+ /** 客户端录制权限变更(角色级, cause.cmd=3260) */
59
+ CLIENT_RECORDING_PERMISSION: 3260,
60
+ /** 客户端录制状态变更 cause.data.widgetCause.cmd=3261 (通过widget属性下发) */
61
+ CLIENT_RECORDING_STATE: 3261,
62
+ /** 用户权限变更(用户级,含客户端录制) */
63
+ USER_PERMISSION_UPDATE: 3121,
56
64
  /** 房间会话请求命令 */
57
65
  ROOM_SESSION_REQUEST: 1001,
58
66
  /** 房间会话接受命令 */
@@ -24,7 +24,9 @@ require("core-js/modules/es.array.includes.js");
24
24
  require("core-js/modules/es.array.push.js");
25
25
  require("core-js/modules/es.json.stringify.js");
26
26
  require("core-js/modules/esnext.iterator.constructor.js");
27
+ require("core-js/modules/esnext.iterator.find.js");
27
28
  require("core-js/modules/esnext.iterator.for-each.js");
29
+ require("core-js/modules/esnext.iterator.some.js");
28
30
  var _imports = require("../imports");
29
31
  var _type = require("../type");
30
32
  var _type2 = require("./type");
@@ -68,7 +70,7 @@ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side
68
70
  */
69
71
  class FcrBaseRoomControlImpl {
70
72
  static {
71
- [_initProto] = _applyDecs(this, [[_imports.trace, 2, "getSyncTimestamp"], [_imports.trace, 2, "getRoomInfo"], [_imports.trace, 2, "getRoomSchedule"], [_imports.trace, 2, "getLocalExDataSyncTypeList"], [_joinDecs, 2, "join"], [_imports.trace, 2, "leave"], [_imports.trace, 2, "release"], [_imports.trace, 2, "start"], [_imports.trace, 2, "end"], [_imports.trace, 2, "close"], [_imports.trace, 2, "getRoomState"], [_imports.trace, 2, "getRoomProperties"], [_imports.trace, 2, "getRoomPropertiesByKeyPath"], [_updateRoomPropertiesDecs, 2, "updateRoomProperties"], [_updateIncrementRoomPropertiesDecs, 2, "updateIncrementRoomProperties"], [_deleteRoomPropertiesDecs, 2, "deleteRoomProperties"], [_startCloudRecordingDecs, 2, "startCloudRecording"], [_imports.trace, 2, "pauseCloudRecording"], [_imports.trace, 2, "resumeCloudRecording"], [_imports.trace, 2, "stopCloudRecording"], [_imports.trace, 2, "getLiveStreamingState"], [_imports.trace, 2, "getLiveStreamingConfig"], [_startLiveStreamingDecs, 2, "startLiveStreaming"], [_updateLiveStreamingLayoutDecs, 2, "updateLiveStreamingLayout"], [_imports.trace, 2, "stopLiveStreaming"], [_imports.trace, 2, "getCloudRecordingState"], [_sendRoomMessageDecs, 2, "sendRoomMessage"]], []).e;
73
+ [_initProto] = _applyDecs(this, [[_imports.trace, 2, "getSyncTimestamp"], [_imports.trace, 2, "getRoomInfo"], [_imports.trace, 2, "getRoomSchedule"], [_imports.trace, 2, "getLocalExDataSyncTypeList"], [_joinDecs, 2, "join"], [_imports.trace, 2, "leave"], [_imports.trace, 2, "release"], [_imports.trace, 2, "start"], [_imports.trace, 2, "end"], [_imports.trace, 2, "close"], [_imports.trace, 2, "getRoomState"], [_imports.trace, 2, "getRoomProperties"], [_imports.trace, 2, "getRoomPropertiesByKeyPath"], [_updateRoomPropertiesDecs, 2, "updateRoomProperties"], [_updateIncrementRoomPropertiesDecs, 2, "updateIncrementRoomProperties"], [_deleteRoomPropertiesDecs, 2, "deleteRoomProperties"], [_startCloudRecordingDecs, 2, "startCloudRecording"], [_imports.trace, 2, "pauseCloudRecording"], [_imports.trace, 2, "resumeCloudRecording"], [_imports.trace, 2, "stopCloudRecording"], [_imports.trace, 2, "startClientRecording"], [_imports.trace, 2, "resumeClientRecording"], [_imports.trace, 2, "stopClientRecording"], [_imports.trace, 2, "getLiveStreamingState"], [_imports.trace, 2, "getLiveStreamingConfig"], [_startLiveStreamingDecs, 2, "startLiveStreaming"], [_updateLiveStreamingLayoutDecs, 2, "updateLiveStreamingLayout"], [_imports.trace, 2, "stopLiveStreaming"], [_imports.trace, 2, "hasActiveClientRecording"], [_imports.trace, 2, "getCloudRecordingState"], [_sendRoomMessageDecs, 2, "sendRoomMessage"]], []).e;
72
74
  }
73
75
  //@internal
74
76
  [(_joinDecs = (0, _imports.trace)(['options']), _updateRoomPropertiesDecs = (0, _imports.trace)(['properties', 'cause']), _updateIncrementRoomPropertiesDecs = (0, _imports.trace)(['increments', 'cause']), _deleteRoomPropertiesDecs = (0, _imports.trace)(['properties', 'cause']), _startCloudRecordingDecs = (0, _imports.trace)(['config']), _startLiveStreamingDecs = (0, _imports.trace)(['data']), _updateLiveStreamingLayoutDecs = (0, _imports.trace)(['layoutType']), _sendRoomMessageDecs = (0, _imports.trace)(['payload', 'guaranteedDelivery']), "logger")] = (_initProto(this), (0, _logger.createLogger)({
@@ -78,6 +80,8 @@ class FcrBaseRoomControlImpl {
78
80
  _joinRoomSuccess = false;
79
81
  _joining = false;
80
82
  _joinState = _helpers.ROOM_CONTROL_CONSTANTS.JOIN_STATE.CANCELED;
83
+ _isClientRecordingOwner = false;
84
+ _isStartingClientRecording = false;
81
85
  constructor(_engine, _scene, _api, _config, _roomType, _chatConnection, _sharedCache, _chatRoomControl) {
82
86
  this._engine = _engine;
83
87
  this._scene = _scene;
@@ -226,8 +230,45 @@ class FcrBaseRoomControlImpl {
226
230
  this._joinState = _helpers.ROOM_CONTROL_CONSTANTS.JOIN_STATE.CANCELED;
227
231
  this._cleanup();
228
232
  }
233
+ // Handle user-level local recording permission change (cmd=3121)
234
+ if (event.cause?.cmd === _helpers.ROOM_MESSAGE_COMMANDS.USER_PERMISSION_UPDATE && event.modifiedUser.userId === this._scene.localUser.getLocalUserId()) {
235
+ const causeData = event.cause?.data;
236
+ if (causeData?.removePermissions?.includes('record:startClientRecording')) {
237
+ if (this.isLocalClientRecordingActive()) {
238
+ this.stopClientRecording().catch(e => {
239
+ this.logger.error(`auto-stop client recording failed: ${e.message}`);
240
+ });
241
+ }
242
+ }
243
+ }
244
+ }
245
+ };
246
+
247
+ // 初始化客户端录制观察者
248
+ this._clientRecordingObserver = {
249
+ onRecorderStateChanged: (_channelId, _uid, state, reason) => {
250
+ this.logger.info(`onRecorderStateChanged: state=${state}, reason=${reason}, sceneId=${this._scene.sceneId}`);
251
+ this._observable.notifyObservers('onClientRecordingStateChanged', this._scene.sceneId, state, reason);
229
252
  }
230
253
  };
254
+
255
+ // 客户端录制观察者由子类(FcrMainRoomControlImpl)注册,基类不注册
256
+ }
257
+
258
+ /**
259
+ * 注册客户端录制观察者。由主房间子类在构造时调用。
260
+ */
261
+ _registerClientRecordingObserver() {
262
+ this._engine.getMediaControl().addClientRecordingObserver(this._clientRecordingObserver);
263
+ this._isClientRecordingOwner = true;
264
+ }
265
+
266
+ /**
267
+ * 移除客户端录制观察者。由 _cleanup 调用时需要判断是否已注册。
268
+ */
269
+ _unregisterClientRecordingObserver() {
270
+ this._engine.getMediaControl().removeClientRecordingObserver(this._clientRecordingObserver);
271
+ this._isClientRecordingOwner = false;
231
272
  }
232
273
 
233
274
  /**
@@ -574,6 +615,89 @@ class FcrBaseRoomControlImpl {
574
615
  await (0, _error.handleRequestError)(() => this._api.stopCloudRecording(this._scene.sceneId), _imports.ErrorModuleCode.FCR_ROOM, 'stop cloud recording failed');
575
616
  return _type.FcrReturnCode.SUCCESS;
576
617
  }
618
+ async startClientRecording(config) {
619
+ // 防止并发调用
620
+ if (this._isStartingClientRecording) {
621
+ this.logger.warn('startClientRecording: already starting, ignoring duplicate call');
622
+ return _type.FcrReturnCode.SUCCESS;
623
+ }
624
+ const localUserId = this._scene.localUser.getLocalUserId();
625
+
626
+ // 标记正在启动本地录制,避免在 API 调用后、SDK 录制开始前的时间窗口内触发错误的提示
627
+ this._isStartingClientRecording = true;
628
+ let serverRecordingStarted = false;
629
+ try {
630
+ await (0, _error.handleRequestError)(() => this._api.startClientRecording(this._scene.sceneId, localUserId), _imports.ErrorModuleCode.FCR_ROOM, 'start client recording failed');
631
+ serverRecordingStarted = true;
632
+
633
+ // 使用 mediaControl 来启动本地录制
634
+ const mediaControl = this._engine.getMediaControl();
635
+ const isScreenShareActive = mediaControl.isScreenShareActive();
636
+ const currentScreenShareSourceId = mediaControl.getCurrentScreenShareSourceId();
637
+
638
+ // 获取本地用户的主流 streamId 作为录制 uid(streamId 是数字,userUuid 可能是十六进制字符串)
639
+ const localStreams = this._scene.getStreamsByUserId(localUserId);
640
+ const mainStream = localStreams.find(s => s.videoSourceType === _imports.AgoraRteVideoSourceType.CAMERA) || localStreams[0];
641
+ if (!mainStream) {
642
+ throw new Error(`No stream found for local user: ${localUserId}`);
643
+ }
644
+ const uid = Number(mainStream.streamId);
645
+ await mediaControl.startClientRecording(this._scene.sceneId, uid, config, isScreenShareActive, currentScreenShareSourceId);
646
+ return _type.FcrReturnCode.SUCCESS;
647
+ } catch (e) {
648
+ // API 调用成功但 SDK 启动失败时,回滚服务端状态
649
+ if (serverRecordingStarted) {
650
+ this.logger.error(`startClientRecording SDK failed, rolling back server state: ${e.message}`);
651
+ this._api.stopClientRecording(this._scene.sceneId, localUserId).catch(rollbackErr => {
652
+ this.logger.error(`startClientRecording rollback failed: ${rollbackErr.message}`);
653
+ });
654
+ }
655
+ throw e;
656
+ } finally {
657
+ this._isStartingClientRecording = false;
658
+ }
659
+ }
660
+ async resumeClientRecording() {
661
+ const localUserId = this._scene.localUser.getLocalUserId();
662
+ await (0, _error.handleRequestError)(() => this._api.resumeClientRecording(this._scene.sceneId, localUserId), _imports.ErrorModuleCode.FCR_ROOM, 'resume client recording failed');
663
+ return _type.FcrReturnCode.SUCCESS;
664
+ }
665
+ async stopClientRecording() {
666
+ const localUserId = this._scene.localUser.getLocalUserId();
667
+
668
+ // Step 1: 使用 mediaControl 的 stopClientRecording(即使失败也继续通知服务端)
669
+ const mediaControl = this._engine.getMediaControl();
670
+ try {
671
+ await mediaControl.stopClientRecording();
672
+ } catch (e) {
673
+ this.logger.error(`mediaControl.stopClientRecording failed: ${e.message}`);
674
+ }
675
+
676
+ // Step 2: 无论 SDK 停止是否成功,都通知服务器停止录制
677
+ await (0, _error.handleRequestError)(() => this._api.stopClientRecording(this._scene.sceneId, localUserId), _imports.ErrorModuleCode.FCR_ROOM, 'stop client recording failed');
678
+ return _type.FcrReturnCode.SUCCESS;
679
+ }
680
+ isLocalClientRecordingActive() {
681
+ // 如果正在启动本地录制,也返回 true,避免在启动过程中触发错误的"其他人开启录制"提示
682
+ const mediaControl = this._engine.getMediaControl();
683
+ return this._isStartingClientRecording || mediaControl.isClientRecordingActive();
684
+ }
685
+ isClientRecordingOwnsCapture() {
686
+ const mediaControl = this._engine.getMediaControl();
687
+ return mediaControl.isClientRecordingOwnsCapture();
688
+ }
689
+ async resumeClientRecordingOwnCapture() {
690
+ const mediaControl = this._engine.getMediaControl();
691
+ await mediaControl.resumeClientRecordingOwnCapture();
692
+ }
693
+ async waitForScreenCaptureStopped() {
694
+ const mediaControl = this._engine.getMediaControl();
695
+ await mediaControl.waitForScreenCaptureStopped();
696
+ }
697
+ releaseClientRecordingOwnCapture() {
698
+ const mediaControl = this._engine.getMediaControl();
699
+ mediaControl.releaseClientRecordingOwnCapture();
700
+ }
577
701
  getLiveStreamingState() {
578
702
  let liveStreamingState = this._scene.getScenePropertiesByKeyPath('live.state');
579
703
  if (liveStreamingState === _helpers.LIVE_STREAMING_STATE_VALUES.STARTED) {
@@ -613,6 +737,11 @@ class FcrBaseRoomControlImpl {
613
737
  await this._liveStreamingAction(() => this._api.stopLiveStreaming(this._scene.sceneId), 'stop live streaming failed');
614
738
  return _type.FcrReturnCode.SUCCESS;
615
739
  }
740
+ hasActiveClientRecording() {
741
+ const userControl = this.getUserControl();
742
+ const users = userControl.getUserList();
743
+ return users.some(user => userControl.getUserClientRecordingState(user.userId));
744
+ }
616
745
  getCloudRecordingState() {
617
746
  const {
618
747
  state,
@@ -641,6 +770,21 @@ class FcrBaseRoomControlImpl {
641
770
  async _cleanup() {
642
771
  this._scene.removeObserver(this._sceneObserver);
643
772
  this._engine.removeObserver(this._engineObserver);
773
+
774
+ // 只有注册过录制观察者的 room control 才拥有录制生命周期,
775
+ // 其他房间(如等候室)的 cleanup 不应停止共享 mediaControl 上的录制。
776
+ if (this._isClientRecordingOwner) {
777
+ this._unregisterClientRecordingObserver();
778
+ try {
779
+ const mediaControl = this._engine.getMediaControl();
780
+ if (mediaControl.isClientRecordingActive()) {
781
+ await mediaControl.stopClientRecording();
782
+ this.logger.info('_cleanup: stopped client recording');
783
+ }
784
+ } catch (e) {
785
+ this.logger.error(`_cleanup: failed to stop client recording: ${e.message}`);
786
+ }
787
+ }
644
788
  this._sharedCache.getRoomCache(this._scene.sceneId).clear();
645
789
  try {
646
790
  return await Promise.all([this._chatRoomControl?.leave(), this.sharingControl?.getBoardControl().close(), this.sharingControl?.getAnnotationControl().close(), this._scene.leave()]);
@@ -60,6 +60,7 @@ class FcrInfinityRoomControlImpl extends _.FcrBaseRoomControlImpl {
60
60
  this._interpreterControl = new _interpreterControl.FcrInterpreterControlImpl(api, scene, config, engine, chatConnection, sharedCache);
61
61
  this._addLogObserver();
62
62
  (0, _boardInitInfoHelper.addBoardInitInfoObserver)(this._scene, this._api, this.logger);
63
+ this._registerClientRecordingObserver();
63
64
  this.logger.info(`initialized, room id: ${this._scene.sceneId}`);
64
65
  }
65
66
  getAnnouncement() {
@@ -114,13 +115,11 @@ class FcrInfinityRoomControlImpl extends _.FcrBaseRoomControlImpl {
114
115
  _onLocalUserPermissionInfoDeleted(roomId, event) {
115
116
  if ((0, _utils.hasBoardWritePermission)(event.permissionInfo)) {
116
117
  this.logger.info(`remove board write permission`);
117
-
118
- // this.getSharingControl().getBoardControl().getMainWindow()?.setWritable(false);
118
+ this.getSharingControl().getBoardControl().getMainWindow()?.setWritable(false);
119
119
  }
120
120
  if ((0, _utils.hasAnnotationWritePermission)(event.permissionInfo)) {
121
121
  this.logger.info(`remove annotation write permission`);
122
-
123
- // this.getSharingControl().getAnnotationControl().getMainWindow()?.setWritable(false);
122
+ this.getSharingControl().getAnnotationControl().getMainWindow()?.setWritable(false);
124
123
  }
125
124
  }
126
125
  _addLogObserver() {
@@ -71,6 +71,7 @@ class FcrMainRoomControlImpl extends _.FcrBaseRoomControlImpl {
71
71
  this._interpreterControl = new _interpreterControl.FcrInterpreterControlImpl(api, scene, config, engine, chatConnection, sharedCache);
72
72
  this._addLogObserver();
73
73
  (0, _boardInitInfoHelper.addBoardInitInfoObserver)(this._scene, this._api, this.logger);
74
+ this._registerClientRecordingObserver();
74
75
  this.logger.info(`initialized, room id: ${this._scene.sceneId}`);
75
76
  }
76
77
 
@@ -138,13 +139,11 @@ class FcrMainRoomControlImpl extends _.FcrBaseRoomControlImpl {
138
139
  _onLocalUserPermissionInfoDeleted(roomId, event) {
139
140
  if ((0, _utils2.hasBoardWritePermission)(event.permissionInfo)) {
140
141
  this.logger.info(`remove board write permission`);
141
-
142
- // this.getSharingControl().getBoardControl().getMainWindow()?.setWritable(false);
142
+ this.getSharingControl().getBoardControl().getMainWindow()?.setWritable(false);
143
143
  }
144
144
  if ((0, _utils2.hasAnnotationWritePermission)(event.permissionInfo)) {
145
145
  this.logger.info(`remove annotation write permission`);
146
-
147
- // this.getSharingControl().getAnnotationControl().getMainWindow()?.setWritable(false);
146
+ this.getSharingControl().getAnnotationControl().getMainWindow()?.setWritable(false);
148
147
  }
149
148
  }
150
149
  _checkIfUpdateAnnotationMainWindow() {
@@ -34,7 +34,7 @@ var _user = require("../../utilities/user");
34
34
  var _validateParams = _interopRequireDefault(require("../../utilities/validate-params"));
35
35
  var _helper = require("./helper");
36
36
  var _type2 = require("./type");
37
- let _initProto, _allowWriteAnnotationDecs, _getSecurityInfoDecs, _getLocalUserPermissionInfoDecs, _enableLockedRoomDecs, _allowShareAndWriteDecs, _allowWriteBoardDecs, _allowWriteBoardByUserIdsDecs, _allowSendChatDecs, _allowStartAudioDecs, _allowStartVideoDecs, _allowJoinWithMutedAudioDecs, _allowJoinWithMutedVideoDecs, _allowWatermarkDecs, _allowChangeUserNameDecs, _allowStartCaptionDecs, _allowStartTranscribingDecs, _allowUpdateSttSourceLanguageDecs, _allowJoinWithPromptSoundDecs, _getRemoteUserPermissionEnableDecs, _allowViewUserCountDecs, _allowHandsUpDecs, _allowSendChatByUserRoleDecs, _allowSendChatByUserIdDecs;
37
+ let _initProto, _allowWriteAnnotationDecs, _getSecurityInfoDecs, _getLocalUserPermissionInfoDecs, _enableLockedRoomDecs, _allowShareAndWriteDecs, _allowWriteBoardDecs, _allowWriteBoardByUserIdsDecs, _allowSendChatDecs, _allowStartAudioDecs, _allowStartVideoDecs, _allowJoinWithMutedAudioDecs, _allowJoinWithMutedVideoDecs, _allowWatermarkDecs, _allowChangeUserNameDecs, _allowStartCaptionDecs, _allowStartTranscribingDecs, _allowUpdateSttSourceLanguageDecs, _allowJoinWithPromptSoundDecs, _allowClientRecordingDecs, _allowClientRecordingByUserIdDecs, _getRemoteUserPermissionEnableDecs, _allowViewUserCountDecs, _allowHandsUpDecs, _allowSendChatByUserRoleDecs, _allowSendChatByUserIdDecs;
38
38
  function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; }
39
39
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
40
40
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
@@ -45,10 +45,10 @@ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side
45
45
  */
46
46
  class FcrPrivilegeControlImpl {
47
47
  static {
48
- [_initProto] = _applyDecs(this, [[_allowWriteAnnotationDecs, 2, "allowWriteAnnotation"], [_getSecurityInfoDecs, 2, "getSecurityInfo"], [_getLocalUserPermissionInfoDecs, 2, "getLocalUserPermissionInfo"], [_enableLockedRoomDecs, 2, "enableLockedRoom"], [_allowShareAndWriteDecs, 2, "allowShareAndWrite"], [_allowWriteBoardDecs, 2, "allowWriteBoard"], [_allowWriteBoardByUserIdsDecs, 2, "allowWriteBoardByUserIds"], [_allowSendChatDecs, 2, "allowSendChat"], [_allowStartAudioDecs, 2, "allowStartAudio"], [_allowStartVideoDecs, 2, "allowStartVideo"], [_allowJoinWithMutedAudioDecs, 2, "allowJoinWithMutedAudio"], [_allowJoinWithMutedVideoDecs, 2, "allowJoinWithMutedVideo"], [_allowWatermarkDecs, 2, "allowWatermark"], [_allowChangeUserNameDecs, 2, "allowChangeUserName"], [_allowStartCaptionDecs, 2, "allowStartCaption"], [_allowStartTranscribingDecs, 2, "allowStartTranscribing"], [_allowUpdateSttSourceLanguageDecs, 2, "allowUpdateSttSourceLanguage"], [_allowJoinWithPromptSoundDecs, 2, "allowJoinWithPromptSound"], [_getRemoteUserPermissionEnableDecs, 2, "getRemoteUserPermissionEnable"], [_allowViewUserCountDecs, 2, "allowViewUserCount"], [_allowHandsUpDecs, 2, "allowHandsUp"], [_allowSendChatByUserRoleDecs, 2, "allowSendChatByUserRole"], [_allowSendChatByUserIdDecs, 2, "allowSendChatByUserId"], [_imports.trace, 2, "release"]], []).e;
48
+ [_initProto] = _applyDecs(this, [[_allowWriteAnnotationDecs, 2, "allowWriteAnnotation"], [_getSecurityInfoDecs, 2, "getSecurityInfo"], [_getLocalUserPermissionInfoDecs, 2, "getLocalUserPermissionInfo"], [_enableLockedRoomDecs, 2, "enableLockedRoom"], [_allowShareAndWriteDecs, 2, "allowShareAndWrite"], [_allowWriteBoardDecs, 2, "allowWriteBoard"], [_allowWriteBoardByUserIdsDecs, 2, "allowWriteBoardByUserIds"], [_allowSendChatDecs, 2, "allowSendChat"], [_allowStartAudioDecs, 2, "allowStartAudio"], [_allowStartVideoDecs, 2, "allowStartVideo"], [_allowJoinWithMutedAudioDecs, 2, "allowJoinWithMutedAudio"], [_allowJoinWithMutedVideoDecs, 2, "allowJoinWithMutedVideo"], [_allowWatermarkDecs, 2, "allowWatermark"], [_allowChangeUserNameDecs, 2, "allowChangeUserName"], [_allowStartCaptionDecs, 2, "allowStartCaption"], [_allowStartTranscribingDecs, 2, "allowStartTranscribing"], [_allowUpdateSttSourceLanguageDecs, 2, "allowUpdateSttSourceLanguage"], [_allowJoinWithPromptSoundDecs, 2, "allowJoinWithPromptSound"], [_allowClientRecordingDecs, 2, "allowClientRecording"], [_allowClientRecordingByUserIdDecs, 2, "allowClientRecordingByUserId"], [_getRemoteUserPermissionEnableDecs, 2, "getRemoteUserPermissionEnable"], [_allowViewUserCountDecs, 2, "allowViewUserCount"], [_allowHandsUpDecs, 2, "allowHandsUp"], [_allowSendChatByUserRoleDecs, 2, "allowSendChatByUserRole"], [_allowSendChatByUserIdDecs, 2, "allowSendChatByUserId"], [_imports.trace, 2, "release"]], []).e;
49
49
  }
50
50
  //@internal
51
- [(_allowWriteAnnotationDecs = (0, _imports.trace)(['enable', 'targetRoles']), _getSecurityInfoDecs = [(0, _imports.trace)(['action']), (0, _validateParams.default)(_schema.fcrSecurityActionSchema)], _getLocalUserPermissionInfoDecs = [(0, _imports.trace)(['action']), (0, _validateParams.default)(_schema.fcrPermissionActionSchema)], _enableLockedRoomDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowShareAndWriteDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowWriteBoardDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowWriteBoardByUserIdsDecs = [(0, _imports.trace)(['enable', 'targetUserIds']), (0, _validateParams.default)(_schema.booleanSchema, _schema.stringArraySchema)], _allowSendChatDecs = [(0, _imports.trace)(['enable', 'targetRoles', 'payload']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema, _schema.fcrSecuritySendChatPayloadSchema)], _allowStartAudioDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowStartVideoDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowJoinWithMutedAudioDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowJoinWithMutedVideoDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowWatermarkDecs = [(0, _imports.trace)(['enable', 'targetRoles', 'lineType']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema, _schema.fcrLineTypeSchema.optional())], _allowChangeUserNameDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowStartCaptionDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowStartTranscribingDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowUpdateSttSourceLanguageDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowJoinWithPromptSoundDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _getRemoteUserPermissionEnableDecs = (0, _imports.trace)(['userId', 'type']), _allowViewUserCountDecs = (0, _imports.trace)(['enable', 'targetRoles']), _allowHandsUpDecs = (0, _imports.trace)(['enable', 'targetRoles']), _allowSendChatByUserRoleDecs = (0, _imports.trace)(['config', 'targetRoles']), _allowSendChatByUserIdDecs = (0, _imports.trace)(['enable', 'userId']), "logger")] = (_initProto(this), (0, _logger.createLogger)({
51
+ [(_allowWriteAnnotationDecs = (0, _imports.trace)(['enable', 'targetRoles']), _getSecurityInfoDecs = [(0, _imports.trace)(['action']), (0, _validateParams.default)(_schema.fcrSecurityActionSchema)], _getLocalUserPermissionInfoDecs = [(0, _imports.trace)(['action']), (0, _validateParams.default)(_schema.fcrPermissionActionSchema)], _enableLockedRoomDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowShareAndWriteDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowWriteBoardDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowWriteBoardByUserIdsDecs = [(0, _imports.trace)(['enable', 'targetUserIds']), (0, _validateParams.default)(_schema.booleanSchema, _schema.stringArraySchema)], _allowSendChatDecs = [(0, _imports.trace)(['enable', 'targetRoles', 'payload']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema, _schema.fcrSecuritySendChatPayloadSchema)], _allowStartAudioDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowStartVideoDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowJoinWithMutedAudioDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowJoinWithMutedVideoDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowWatermarkDecs = [(0, _imports.trace)(['enable', 'targetRoles', 'lineType']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema, _schema.fcrLineTypeSchema.optional())], _allowChangeUserNameDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowStartCaptionDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowStartTranscribingDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowUpdateSttSourceLanguageDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowJoinWithPromptSoundDecs = [(0, _imports.trace)(['enable', 'targetRoles']), (0, _validateParams.default)(_schema.booleanSchema, _schema.fcrPrivilegeUserRoleArraySchema)], _allowClientRecordingDecs = (0, _imports.trace)(['permissionType']), _allowClientRecordingByUserIdDecs = (0, _imports.trace)(['userId', 'enable']), _getRemoteUserPermissionEnableDecs = (0, _imports.trace)(['userId', 'type']), _allowViewUserCountDecs = (0, _imports.trace)(['enable', 'targetRoles']), _allowHandsUpDecs = (0, _imports.trace)(['enable', 'targetRoles']), _allowSendChatByUserRoleDecs = (0, _imports.trace)(['config', 'targetRoles']), _allowSendChatByUserIdDecs = (0, _imports.trace)(['enable', 'userId']), "logger")] = (_initProto(this), (0, _logger.createLogger)({
52
52
  prefix: 'FcrPrivilegeControlImpl'
53
53
  }));
54
54
  _observable = new _imports.AgoraObservable();
@@ -260,6 +260,25 @@ class FcrPrivilegeControlImpl {
260
260
  }), _imports.ErrorModuleCode.FCR_ROOM_PRIVILEGE, 'allow join with prompt sound failed');
261
261
  return _type.FcrReturnCode.SUCCESS;
262
262
  }
263
+ async allowClientRecording(permissionType) {
264
+ const enable = permissionType === _type2.FcrClientRecordingPermissionType.HostOnly ? 0 : 1;
265
+ const targetRoles = permissionType === _type2.FcrClientRecordingPermissionType.AllMembers ? [_type2.FcrPrivilegeUserRoleToStringMap[_type2.FcrPrivilegeUserRole.PARTICIPANT]] : undefined;
266
+ await (0, _error.handleRequestError)(() => this._api.allowClientRecording({
267
+ roomId: this._scene.sceneId,
268
+ permissionType,
269
+ enable,
270
+ targetRoles
271
+ }), _imports.ErrorModuleCode.FCR_ROOM_PRIVILEGE, 'allow client recording failed');
272
+ return _type.FcrReturnCode.SUCCESS;
273
+ }
274
+ async allowClientRecordingByUserId(userId, enable) {
275
+ await (0, _error.handleRequestError)(() => this._api.allowClientRecordingByUserId({
276
+ roomId: this._scene.sceneId,
277
+ toUserUuids: [userId],
278
+ enable
279
+ }), _imports.ErrorModuleCode.FCR_ROOM_PRIVILEGE, 'allow client recording by user id failed');
280
+ return _type.FcrReturnCode.SUCCESS;
281
+ }
263
282
  getRemoteUserPermissionEnable(userId, type) {
264
283
  if (userId === this._scene.localUser.getLocalUserId()) {
265
284
  return false;
@@ -133,6 +133,17 @@ export interface FcrPrivilegeControl {
133
133
  * @param targetRoles
134
134
  */
135
135
  allowJoinWithPromptSound(enable: boolean, targetRoles: FcrPrivilegeUserRole[]): Promise<number>;
136
+ /**
137
+ * Sets client recording permission type (role-level).
138
+ * @param permissionType Permission type
139
+ */
140
+ allowClientRecording(permissionType: FcrClientRecordingPermissionType): Promise<number>;
141
+ /**
142
+ * Allows or disallows a specific user to do client recording.
143
+ * @param userId Target user ID
144
+ * @param enable true to allow, false to disallow
145
+ */
146
+ allowClientRecordingByUserId(userId: string, enable: boolean): Promise<number>;
136
147
  /**
137
148
  * Gets the remote user's permission enable.
138
149
  * @param userId
@@ -358,6 +369,9 @@ export declare enum FcrPermissionAction {
358
369
  RecordStopCloudRecording = "record:stopCloudRecording",
359
370
  RecordPauseCloudRecording = "record:pauseCloudRecording",
360
371
  RecordResumeCloudRecording = "record:resumeCloudRecording",
372
+ RecordStartClientRecording = "record:startClientRecording",
373
+ RecordStopClientRecording = "record:stopClientRecording",
374
+ RecordSetClientRecordingPermission = "record:setClientRecordingPermission",
361
375
  CallCallIn = "call:callIn",
362
376
  CallHangUp = "call:hangUp",
363
377
  CallInvite = "call:invite",
@@ -429,6 +443,7 @@ export declare enum FcrSecurityAction {
429
443
  SttStartCaption = "caption",
430
444
  SttStartTranscribe = "transcribe",
431
445
  JoinWithPromptSound = "joinWithPromptSound",
446
+ ClientRecording = "clientRecording",
432
447
  HandsUp = "handsUp",
433
448
  ViewUserCount = "viewUserCount"
434
449
  }
@@ -436,6 +451,11 @@ export declare enum FcrWatermarkPermissionState {
436
451
  ON = 1,
437
452
  OFF = 0
438
453
  }
454
+ export declare enum FcrClientRecordingPermissionType {
455
+ HostOnly = 1,
456
+ AllMembers = 2,
457
+ SpecifiedMembers = 3
458
+ }
439
459
  export declare enum FcrRemotePermissionType {
440
460
  SendChatToAll = 1,
441
461
  SendChatToCoParticipant = 2,
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.FcrWatermarkPermissionState = exports.FcrTargetUserTypeToStringMap = exports.FcrTargetUserType = exports.FcrSecurityAction = exports.FcrRoleFilter = exports.FcrRemotePermissionType = exports.FcrPrivilegeUserStringToRoleMap = exports.FcrPrivilegeUserRoleToStringMap = exports.FcrPrivilegeUserRole = exports.FcrPermissionAction = exports.FcrOperatePermissionType = exports.FcrExDataSyncType = exports.FcrChatPermissionType = void 0;
6
+ exports.FcrWatermarkPermissionState = exports.FcrTargetUserTypeToStringMap = exports.FcrTargetUserType = exports.FcrSecurityAction = exports.FcrRoleFilter = exports.FcrRemotePermissionType = exports.FcrPrivilegeUserStringToRoleMap = exports.FcrPrivilegeUserRoleToStringMap = exports.FcrPrivilegeUserRole = exports.FcrPermissionAction = exports.FcrOperatePermissionType = exports.FcrExDataSyncType = exports.FcrClientRecordingPermissionType = exports.FcrChatPermissionType = void 0;
7
7
  let FcrTargetUserType = exports.FcrTargetUserType = /*#__PURE__*/function (FcrTargetUserType) {
8
8
  FcrTargetUserType[FcrTargetUserType["SELF"] = 1] = "SELF";
9
9
  FcrTargetUserType[FcrTargetUserType["HOST"] = 2] = "HOST";
@@ -118,6 +118,9 @@ let FcrPermissionAction = exports.FcrPermissionAction = /*#__PURE__*/function (F
118
118
  FcrPermissionAction["RecordStopCloudRecording"] = "record:stopCloudRecording";
119
119
  FcrPermissionAction["RecordPauseCloudRecording"] = "record:pauseCloudRecording";
120
120
  FcrPermissionAction["RecordResumeCloudRecording"] = "record:resumeCloudRecording";
121
+ FcrPermissionAction["RecordStartClientRecording"] = "record:startClientRecording";
122
+ FcrPermissionAction["RecordStopClientRecording"] = "record:stopClientRecording";
123
+ FcrPermissionAction["RecordSetClientRecordingPermission"] = "record:setClientRecordingPermission";
121
124
  FcrPermissionAction["CallCallIn"] = "call:callIn";
122
125
  FcrPermissionAction["CallHangUp"] = "call:hangUp";
123
126
  FcrPermissionAction["CallInvite"] = "call:invite";
@@ -183,6 +186,7 @@ let FcrSecurityAction = exports.FcrSecurityAction = /*#__PURE__*/function (FcrSe
183
186
  FcrSecurityAction["SttStartCaption"] = "caption";
184
187
  FcrSecurityAction["SttStartTranscribe"] = "transcribe";
185
188
  FcrSecurityAction["JoinWithPromptSound"] = "joinWithPromptSound";
189
+ FcrSecurityAction["ClientRecording"] = "clientRecording";
186
190
  FcrSecurityAction["HandsUp"] = "handsUp";
187
191
  FcrSecurityAction["ViewUserCount"] = "viewUserCount";
188
192
  return FcrSecurityAction;
@@ -192,6 +196,12 @@ let FcrWatermarkPermissionState = exports.FcrWatermarkPermissionState = /*#__PUR
192
196
  FcrWatermarkPermissionState[FcrWatermarkPermissionState["OFF"] = 0] = "OFF";
193
197
  return FcrWatermarkPermissionState;
194
198
  }({});
199
+ let FcrClientRecordingPermissionType = exports.FcrClientRecordingPermissionType = /*#__PURE__*/function (FcrClientRecordingPermissionType) {
200
+ FcrClientRecordingPermissionType[FcrClientRecordingPermissionType["HostOnly"] = 1] = "HostOnly";
201
+ FcrClientRecordingPermissionType[FcrClientRecordingPermissionType["AllMembers"] = 2] = "AllMembers";
202
+ FcrClientRecordingPermissionType[FcrClientRecordingPermissionType["SpecifiedMembers"] = 3] = "SpecifiedMembers";
203
+ return FcrClientRecordingPermissionType;
204
+ }({});
195
205
  let FcrRemotePermissionType = exports.FcrRemotePermissionType = /*#__PURE__*/function (FcrRemotePermissionType) {
196
206
  FcrRemotePermissionType[FcrRemotePermissionType["SendChatToAll"] = 1] = "SendChatToAll";
197
207
  FcrRemotePermissionType[FcrRemotePermissionType["SendChatToCoParticipant"] = 2] = "SendChatToCoParticipant";
@@ -1,4 +1,5 @@
1
1
  import { FcrError } from '..';
2
+ import { FcrClientRecordingConfig, FcrRecorderState, FcrRecorderReasonCode } from '../imports';
2
3
  import { FcrRoomJoinOptionsSchema } from '../schema';
3
4
  import { FcrCloudRecordingConfig, FcrLiveStreamingConfig, FcrLiveStreamingLayoutType, FcrLiveStreamingState, FcrMessage, FcrNetworkQualityEvent, FcrNetworkStats, FcrRecordingState, FcrRoomInfo, FcrRoomJoinSnapshotOptions, FcrRoomPropertiesDeletedEvent, FcrRoomPropertiesUpdatedEvent, FcrRoomRouteSwitchEvent, FcrRoomSchedule, FcrRoomState } from '../type';
4
5
  import { FcrChatRoomControl } from './chatroom-control/type';
@@ -168,6 +169,42 @@ export interface FcrBaseRoomControl {
168
169
  * Gets the state of the cloud recording.
169
170
  */
170
171
  getCloudRecordingState(): FcrRecordingState;
172
+ /**
173
+ * Starts the client recording.
174
+ * @param config Optional recording configuration (screenSourceId, reuseScreenShare, storagePath, etc.)
175
+ */
176
+ startClientRecording(config?: FcrClientRecordingConfig): Promise<number>;
177
+ /**
178
+ * Stops the client recording.
179
+ */
180
+ stopClientRecording(): Promise<number>;
181
+ /**
182
+ * Check whether client recording is currently active.
183
+ * Screen share should call this before stopping capture — if true, keep capture alive.
184
+ */
185
+ isLocalClientRecordingActive(): boolean;
186
+ /**
187
+ * Check whether client recording owns its own screen capture.
188
+ */
189
+ isClientRecordingOwnsCapture(): boolean;
190
+ /**
191
+ * Resume recording's own window capture after it was released.
192
+ */
193
+ resumeClientRecordingOwnCapture(): Promise<void>;
194
+ /**
195
+ * Wait for the SDK's screen capture stopped callback.
196
+ * Must be called BEFORE stopScreenCapture to register the promise.
197
+ */
198
+ waitForScreenCaptureStopped(): Promise<void>;
199
+ /**
200
+ * Release the recording's own screen/window capture without stopping the recording.
201
+ * Called before starting screen share to free the capture resource.
202
+ */
203
+ releaseClientRecordingOwnCapture(): void;
204
+ /**
205
+ * Checks whether any user in the room is currently doing client recording.
206
+ */
207
+ hasActiveClientRecording(): boolean;
171
208
  /**
172
209
  * Sends a message to the room.
173
210
  * @param payload
@@ -303,6 +340,13 @@ export type FcrRoomObserver = {
303
340
  * @param type
304
341
  */
305
342
  onLocalExDataSyncTypeRemoved?(roomId: string, syncTypeInfo: FcrExDataSyncType): void;
343
+ /**
344
+ * Callback to receive the client recording state changed event.
345
+ * @param roomId The room ID
346
+ * @param state The recorder state
347
+ * @param reason The reason for the state change
348
+ */
349
+ onClientRecordingStateChanged?(roomId: string, state: FcrRecorderState, reason: FcrRecorderReasonCode): void;
306
350
  };
307
351
  export type FcrWaitingRoomObserver = FcrRoomObserver & {};
308
352
  export type FcrJoinBeforeHostWaitingRoomObserver = FcrRoomObserver & {};