@reactoo/watchtogether-sdk-js 2.5.32 → 2.5.38
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/dist/watchtogether-sdk.js +20 -20
- package/dist/watchtogether-sdk.min.js +2 -2
- package/example/bulk_join_room/bulk_join_room_2.css +57 -0
- package/example/bulk_join_room/bulk_join_room_2.html +528 -0
- package/example/bulk_join_room/sound_2.mp3 +0 -0
- package/example/index.html +7 -4
- package/package.json +1 -1
- package/src/models/room-session.js +32 -14
- package/src/models/room.js +13 -11
- package/src/modules/sync-modules/sync-dash-vod.js +1 -1
- package/src/modules/sync-modules/sync-dash.js +1 -1
- package/src/modules/sync-modules/sync-dazn-dash.js +1 -1
- package/src/modules/sync-modules/sync-doris.js +1 -1
- package/src/modules/sync-modules/sync-hls-vod.js +1 -1
- package/src/modules/sync-modules/sync-hls.js +1 -1
- package/src/modules/sync-modules/sync-native-hls-vod.js +1 -1
- package/src/modules/sync-modules/sync-native-hls.js +1 -1
- package/src/modules/sync-modules/sync-shaka-dash-vod.js +1 -1
- package/src/modules/sync-modules/sync-shaka-dash.js +1 -1
- package/src/modules/sync-modules/sync-universal.js +1 -1
- package/src/modules/wt-iot.js +14 -7
- package/src/modules/wt-room.js +238 -127
- package/src/modules/wt-utils.js +23 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @reactoo/watchtogether-sdk-js
|
|
3
|
-
* @version 2.5.
|
|
3
|
+
* @version 2.5.38
|
|
4
4
|
*/
|
|
5
5
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
6
6
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
@@ -3603,7 +3603,7 @@ eval("var getBuiltIn = __webpack_require__(/*! ../internals/get-built-in */ \"./
|
|
|
3603
3603
|
/*! no static exports found */
|
|
3604
3604
|
/***/ (function(module, exports, __webpack_require__) {
|
|
3605
3605
|
|
|
3606
|
-
eval("var bind = __webpack_require__(/*! ../internals/function-bind-context */ \"./node_modules/core-js-pure/internals/function-bind-context.js\");\n\nvar call = __webpack_require__(/*! ../internals/function-call */ \"./node_modules/core-js-pure/internals/function-call.js\");\n\nvar anObject = __webpack_require__(/*! ../internals/an-object */ \"./node_modules/core-js-pure/internals/an-object.js\");\n\nvar tryToString = __webpack_require__(/*! ../internals/try-to-string */ \"./node_modules/core-js-pure/internals/try-to-string.js\");\n\nvar isArrayIteratorMethod = __webpack_require__(/*! ../internals/is-array-iterator-method */ \"./node_modules/core-js-pure/internals/is-array-iterator-method.js\");\n\nvar lengthOfArrayLike = __webpack_require__(/*! ../internals/length-of-array-like */ \"./node_modules/core-js-pure/internals/length-of-array-like.js\");\n\nvar isPrototypeOf = __webpack_require__(/*! ../internals/object-is-prototype-of */ \"./node_modules/core-js-pure/internals/object-is-prototype-of.js\");\n\nvar getIterator = __webpack_require__(/*! ../internals/get-iterator */ \"./node_modules/core-js-pure/internals/get-iterator.js\");\n\nvar getIteratorMethod = __webpack_require__(/*! ../internals/get-iterator-method */ \"./node_modules/core-js-pure/internals/get-iterator-method.js\");\n\nvar iteratorClose = __webpack_require__(/*! ../internals/iterator-close */ \"./node_modules/core-js-pure/internals/iterator-close.js\");\n\nvar $TypeError = TypeError;\n\nvar Result = function (stopped, result) {\n this.stopped = stopped;\n this.result = result;\n};\n\nvar ResultPrototype = Result.prototype;\n\nmodule.exports = function (iterable, unboundFunction, options) {\n var that = options && options.that;\n var AS_ENTRIES = !!(options && options.AS_ENTRIES);\n var IS_ITERATOR = !!(options && options.IS_ITERATOR);\n var INTERRUPTED = !!(options && options.INTERRUPTED);\n var fn = bind(unboundFunction, that);\n var iterator, iterFn, index, length, result, next, step;\n\n var stop = function (condition) {\n if (iterator) iteratorClose(iterator, 'normal', condition);\n return new Result(true, condition);\n };\n\n var callFn = function (value) {\n if (AS_ENTRIES) {\n anObject(value);\n return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);\n }\n\n return INTERRUPTED ? fn(value, stop) : fn(value);\n };\n\n if (IS_ITERATOR) {\n iterator = iterable;\n } else {\n iterFn = getIteratorMethod(iterable);\n if (!iterFn) throw $TypeError(tryToString(iterable) + ' is not iterable'); // optimisation for array iterators\n\n if (isArrayIteratorMethod(iterFn)) {\n for (index = 0, length = lengthOfArrayLike(iterable); length > index; index++) {\n result = callFn(iterable[index]);\n if (result && isPrototypeOf(ResultPrototype, result)) return result;\n }\n\n return new Result(false);\n }\n\n iterator = getIterator(iterable, iterFn);\n }\n\n next = iterator.next;\n\n while (!(step = call(next, iterator)).done) {\n try {\n result = callFn(step.value);\n } catch (error) {\n iteratorClose(iterator, 'throw', error);\n }\n\n if (typeof result == 'object' && result && isPrototypeOf(ResultPrototype, result)) return result;\n }\n\n return new Result(false);\n};\n\n//# sourceURL=webpack://WatchTogetherSDK/./node_modules/core-js-pure/internals/iterate.js?");
|
|
3606
|
+
eval("var bind = __webpack_require__(/*! ../internals/function-bind-context */ \"./node_modules/core-js-pure/internals/function-bind-context.js\");\n\nvar call = __webpack_require__(/*! ../internals/function-call */ \"./node_modules/core-js-pure/internals/function-call.js\");\n\nvar anObject = __webpack_require__(/*! ../internals/an-object */ \"./node_modules/core-js-pure/internals/an-object.js\");\n\nvar tryToString = __webpack_require__(/*! ../internals/try-to-string */ \"./node_modules/core-js-pure/internals/try-to-string.js\");\n\nvar isArrayIteratorMethod = __webpack_require__(/*! ../internals/is-array-iterator-method */ \"./node_modules/core-js-pure/internals/is-array-iterator-method.js\");\n\nvar lengthOfArrayLike = __webpack_require__(/*! ../internals/length-of-array-like */ \"./node_modules/core-js-pure/internals/length-of-array-like.js\");\n\nvar isPrototypeOf = __webpack_require__(/*! ../internals/object-is-prototype-of */ \"./node_modules/core-js-pure/internals/object-is-prototype-of.js\");\n\nvar getIterator = __webpack_require__(/*! ../internals/get-iterator */ \"./node_modules/core-js-pure/internals/get-iterator.js\");\n\nvar getIteratorMethod = __webpack_require__(/*! ../internals/get-iterator-method */ \"./node_modules/core-js-pure/internals/get-iterator-method.js\");\n\nvar iteratorClose = __webpack_require__(/*! ../internals/iterator-close */ \"./node_modules/core-js-pure/internals/iterator-close.js\");\n\nvar $TypeError = TypeError;\n\nvar Result = function (stopped, result) {\n this.stopped = stopped;\n this.result = result;\n};\n\nvar ResultPrototype = Result.prototype;\n\nmodule.exports = function (iterable, unboundFunction, options) {\n var that = options && options.that;\n var AS_ENTRIES = !!(options && options.AS_ENTRIES);\n var IS_RECORD = !!(options && options.IS_RECORD);\n var IS_ITERATOR = !!(options && options.IS_ITERATOR);\n var INTERRUPTED = !!(options && options.INTERRUPTED);\n var fn = bind(unboundFunction, that);\n var iterator, iterFn, index, length, result, next, step;\n\n var stop = function (condition) {\n if (iterator) iteratorClose(iterator, 'normal', condition);\n return new Result(true, condition);\n };\n\n var callFn = function (value) {\n if (AS_ENTRIES) {\n anObject(value);\n return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);\n }\n\n return INTERRUPTED ? fn(value, stop) : fn(value);\n };\n\n if (IS_RECORD) {\n iterator = iterable.iterator;\n } else if (IS_ITERATOR) {\n iterator = iterable;\n } else {\n iterFn = getIteratorMethod(iterable);\n if (!iterFn) throw $TypeError(tryToString(iterable) + ' is not iterable'); // optimisation for array iterators\n\n if (isArrayIteratorMethod(iterFn)) {\n for (index = 0, length = lengthOfArrayLike(iterable); length > index; index++) {\n result = callFn(iterable[index]);\n if (result && isPrototypeOf(ResultPrototype, result)) return result;\n }\n\n return new Result(false);\n }\n\n iterator = getIterator(iterable, iterFn);\n }\n\n next = IS_RECORD ? iterable.next : iterator.next;\n\n while (!(step = call(next, iterator)).done) {\n try {\n result = callFn(step.value);\n } catch (error) {\n iteratorClose(iterator, 'throw', error);\n }\n\n if (typeof result == 'object' && result && isPrototypeOf(ResultPrototype, result)) return result;\n }\n\n return new Result(false);\n};\n\n//# sourceURL=webpack://WatchTogetherSDK/./node_modules/core-js-pure/internals/iterate.js?");
|
|
3607
3607
|
|
|
3608
3608
|
/***/ }),
|
|
3609
3609
|
|
|
@@ -4104,7 +4104,7 @@ eval("var global = __webpack_require__(/*! ../internals/global */ \"./node_modul
|
|
|
4104
4104
|
/*! no static exports found */
|
|
4105
4105
|
/***/ (function(module, exports, __webpack_require__) {
|
|
4106
4106
|
|
|
4107
|
-
eval("var IS_PURE = __webpack_require__(/*! ../internals/is-pure */ \"./node_modules/core-js-pure/internals/is-pure.js\");\n\nvar store = __webpack_require__(/*! ../internals/shared-store */ \"./node_modules/core-js-pure/internals/shared-store.js\");\n\n(module.exports = function (key, value) {\n return store[key] || (store[key] = value !== undefined ? value : {});\n})('versions', []).push({\n version: '3.23.
|
|
4107
|
+
eval("var IS_PURE = __webpack_require__(/*! ../internals/is-pure */ \"./node_modules/core-js-pure/internals/is-pure.js\");\n\nvar store = __webpack_require__(/*! ../internals/shared-store */ \"./node_modules/core-js-pure/internals/shared-store.js\");\n\n(module.exports = function (key, value) {\n return store[key] || (store[key] = value !== undefined ? value : {});\n})('versions', []).push({\n version: '3.23.5',\n mode: IS_PURE ? 'pure' : 'global',\n copyright: '© 2014-2022 Denis Pushkarev (zloirock.ru)',\n license: 'https://github.com/zloirock/core-js/blob/v3.23.5/LICENSE',\n source: 'https://github.com/zloirock/core-js'\n});\n\n//# sourceURL=webpack://WatchTogetherSDK/./node_modules/core-js-pure/internals/shared.js?");
|
|
4108
4108
|
|
|
4109
4109
|
/***/ }),
|
|
4110
4110
|
|
|
@@ -9128,7 +9128,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n\n\nlet iot = function () {\n
|
|
|
9128
9128
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9129
9129
|
|
|
9130
9130
|
"use strict";
|
|
9131
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _modules_wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/wt-utils */ \"./src/modules/wt-utils.js\");\n/* harmony import */ var _modules_wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/wt-emitter */ \"./src/modules/wt-emitter.js\");\n/* harmony import */ var _modules_sync_modules_sync_hls__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/sync-modules/sync-hls */ \"./src/modules/sync-modules/sync-hls.js\");\n/* harmony import */ var _modules_sync_modules_sync_hls_vod__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../modules/sync-modules/sync-hls-vod */ \"./src/modules/sync-modules/sync-hls-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_native_hls_vod__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/sync-modules/sync-native-hls-vod */ \"./src/modules/sync-modules/sync-native-hls-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_native_hls__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../modules/sync-modules/sync-native-hls */ \"./src/modules/sync-modules/sync-native-hls.js\");\n/* harmony import */ var _modules_sync_modules_sync_shaka_dash__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../modules/sync-modules/sync-shaka-dash */ \"./src/modules/sync-modules/sync-shaka-dash.js\");\n/* harmony import */ var _modules_sync_modules_sync_shaka_dash_vod__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../modules/sync-modules/sync-shaka-dash-vod */ \"./src/modules/sync-modules/sync-shaka-dash-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_dash__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../modules/sync-modules/sync-dash */ \"./src/modules/sync-modules/sync-dash.js\");\n/* harmony import */ var _modules_sync_modules_sync_dash_vod__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../modules/sync-modules/sync-dash-vod */ \"./src/modules/sync-modules/sync-dash-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_doris__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../modules/sync-modules/sync-doris */ \"./src/modules/sync-modules/sync-doris.js\");\n/* harmony import */ var _modules_sync_modules_sync_disabled__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../modules/sync-modules/sync-disabled */ \"./src/modules/sync-modules/sync-disabled.js\");\n/* harmony import */ var _modules_sync_modules_sync_dazn_dash__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../modules/sync-modules/sync-dazn-dash */ \"./src/modules/sync-modules/sync-dazn-dash.js\");\n/* harmony import */ var _modules_sync_modules_sync_universal__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../modules/sync-modules/sync-universal */ \"./src/modules/sync-modules/sync-universal.js\");\n\n\n\n // SYNCHRONISATION MODULES\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nlet roomSession = function (_ref, room, wt) {\n let {\n roomId,\n pinHash,\n isTalkback,\n isMonitor,\n isInstructor\n } = _ref;\n let primaryRoomId = roomId;\n let publicCustomEvents = ['changePlayerSource', 'chatMessage', 'userUpdate', 'reconnecting', 'connecting', 'remoteMuted', 'scaling'];\n\n const addEvents = events => {\n publicCustomEvents = [...publicCustomEvents, ...events];\n };\n\n const removeEvents = events => {\n publicCustomEvents = publicCustomEvents.filter(ev => events.indexOf(ev) === -1);\n };\n\n const emitter = Object(_modules_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n\n let alpTimeoutId = null;\n\n let ___; // return object\n\n\n room.on('addLocalParticipant', () => {\n // TODO: this doesnt seem to be fixable until we switch to different type of messaging\n // At some random case we don't get message back if we don't wait\n clearTimeout(alpTimeoutId);\n alpTimeoutId = setTimeout(() => {\n ___.__requestMuteStatus();\n }, 2000);\n });\n room.on('localMuted', _ref2 => {\n let {\n type,\n value\n } = _ref2;\n\n ___.sendSystemMessage('remote_muted', {\n type,\n value\n });\n });\n room.on('data', data => {\n ___.__parseDataEvents(data);\n });\n return ___ = {\n syncModule: null,\n playerInterface: null,\n\n get userId() {\n return room.userId;\n },\n\n get roomId() {\n return roomId;\n },\n\n get sessionId() {\n return room.sessionId;\n },\n\n get constructId() {\n return room.constructId;\n },\n\n destroy: function () {\n clearTimeout(alpTimeoutId);\n this.detachPlayer();\n return room.destroy().finally(() => {\n this.$clear();\n return true;\n });\n },\n iceRestart: function () {\n return room._iceRestart(room.handleId);\n },\n connect: function () {\n let {\n reactooRoomId = null\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n emitter.emit('connecting', true);\n clearTimeout(alpTimeoutId);\n return Promise.all([wt.room.__joinRoom({\n roomId: reactooRoomId || primaryRoomId,\n pinHash,\n isTalkback,\n isMonitor,\n isInstructor\n }), wt.user.getUserSelf()]).then(_ref3 => {\n var _roomData$data;\n\n let [roomData, userData] = _ref3;\n\n // Happens when we reroute user to a different room\n if ((roomData === null || roomData === void 0 ? void 0 : (_roomData$data = roomData.data) === null || _roomData$data === void 0 ? void 0 : _roomData$data.reactooRoomId) !== roomId) {\n roomId = roomData.data.reactooRoomId;\n emitter.emit('changeRoomId', roomId);\n }\n\n return Promise.all([roomData, userData]);\n }).then(_ref4 => {\n let [roomData, userData] = _ref4;\n return Promise.all([roomData, userData, this.setRoomVars()]);\n }).then(_ref5 => {\n let [roomData, userData, _] = _ref5;\n return Promise.all([roomData, userData, room.connect(roomData.data.roomId, roomData.data.pin, roomData.data.href, roomData.data.iceServers, roomData.data.accessToken, roomData.data.display, roomData.data.userId, roomData.data.webrtcVersion, roomData.data.bitrate ? parseInt(roomData.data.bitrate) : 0, isMonitor, roomData.data.recordingFilename)]);\n }).finally(() => {\n emitter.emit('connecting', false);\n });\n },\n disconnect: function (dontWaitForResponses) {\n clearTimeout(alpTimeoutId);\n return room.disconnect(dontWaitForResponses);\n },\n //TODO: refactor restart method\n restart: function () {\n var _handle$webrtcStuff;\n\n let {\n isObserver = false,\n reactooRoomId = null\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n emitter.emit('reconnecting', true);\n room.isRestarting = true;\n let wasPublished = room._isPublished;\n\n let handle = room._getHandle(room.handleId);\n\n let stream = null;\n\n if (handle !== null && handle !== void 0 && (_handle$webrtcStuff = handle.webrtcStuff) !== null && _handle$webrtcStuff !== void 0 && _handle$webrtcStuff.stream && wasPublished) {\n stream = handle.webrtcStuff.stream;\n }\n\n return this.disconnect().then(() => Object(_modules_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(1000)) //TODO: remove 1000ms wait by waiting for proper events from janus\n .then(() => this.connect({\n reactooRoomId\n })).then(() => {\n if (isObserver) {\n return this.publishLocal(null);\n } else if (stream) {\n return this.publishLocal(stream);\n } else return Promise.resolve();\n }).then(() => {\n room.isRestarting = false;\n emitter.emit('reconnecting', false);\n return 1;\n }).catch(error => {\n room.isRestarting = false;\n emitter.emit('reconnecting', false);\n emitter.emit('error', {\n type: 'error',\n id: 26,\n message: 'reconnecting failed',\n data: error\n });\n return Promise.reject(0);\n });\n },\n getStats: function () {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n return room._getStats(type);\n },\n getRoomParticipants: function () {\n return room._participants;\n },\n __parseDataEvents: function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'message') {\n if (msg.action === 'pending_shutdown' || msg.action === 'shutting_down') {\n emitter.emit('scaling');\n this.restart(); // current videoroom was reassigned to a different WebRTC instance\n } else if (msg.action === 'force_restart') {\n this.restart();\n } else if (msg.action === 'user_update_displayname' || msg.action === 'user_update_avatar' || msg.action === 'user_update_customattributes' || msg.action === 'user_update_privateattributes') {\n emitter.emit('userUpdate', msg.text);\n } else if (msg.action === 'observer_connecting' || msg.action === 'talkback_connecting' || msg.action === 'instructor_connecting') {\n this.setRoomVars().catch(e => {\n room._log('Setting observers failed, this will cause issues', e);\n });\n } else if (msg.action === 'bitrate_changed') {\n this.setBitrateCap(msg.text);\n } else if (msg.user_action === 'chat_message') {\n emitter.emit('chatMessage', msg);\n } else if (msg.user_action === 'remote_muted') {\n if (msg.from !== room.userId) {\n emitter.emit('remoteMuted', {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n });\n }\n } else if (msg.user_action === 'remote_muted_request') {\n if (msg.from !== room.userId) {\n this.__sendMuteStatus();\n }\n }\n }\n },\n renderPlayer: function (playerWrapper, fullscreenElement, roomId) {\n try {\n this.syncModule = Object(_modules_sync_modules_sync_universal__WEBPACK_IMPORTED_MODULE_13__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n this.playerInterface = wt.__privates.playerFactory(playerWrapper, fullscreenElement, this.syncModule.getHandlers(), {\n roomId\n });\n this.syncModule.initialize({\n playerInterface: this.playerInterface\n });\n return true;\n } catch (e) {\n return false;\n }\n },\n attachPlayer: function (type, inputs) {\n this.detachPlayer();\n\n if (type === 'hlsjs') {\n this.syncModule = Object(_modules_sync_modules_sync_hls__WEBPACK_IMPORTED_MODULE_2__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'hlsjs-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_hls_vod__WEBPACK_IMPORTED_MODULE_3__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'hlsnative-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_native_hls_vod__WEBPACK_IMPORTED_MODULE_4__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'hlsnative') {\n this.syncModule = Object(_modules_sync_modules_sync_native_hls__WEBPACK_IMPORTED_MODULE_5__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'shaka-dash-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_shaka_dash_vod__WEBPACK_IMPORTED_MODULE_7__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'shaka-dash') {\n this.syncModule = Object(_modules_sync_modules_sync_shaka_dash__WEBPACK_IMPORTED_MODULE_6__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'dashjs') {\n this.syncModule = Object(_modules_sync_modules_sync_dash__WEBPACK_IMPORTED_MODULE_8__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'dashjs-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_dash_vod__WEBPACK_IMPORTED_MODULE_9__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'doris') {\n this.syncModule = Object(_modules_sync_modules_sync_doris__WEBPACK_IMPORTED_MODULE_10__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'dazn-dash') {\n this.syncModule = Object(_modules_sync_modules_sync_dazn_dash__WEBPACK_IMPORTED_MODULE_12__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'disabled') {\n this.syncModule = Object(_modules_sync_modules_sync_disabled__WEBPACK_IMPORTED_MODULE_11__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else {\n room._log('Synchronisation type not recognised');\n }\n },\n detachPlayer: function () {\n if (this.syncModule) {\n this.playerInterface = null;\n this.syncModule.destroy();\n\n if (this.syncModule.__events) {\n removeEvents(this.syncModule.__events);\n }\n\n this.syncModule = null;\n }\n },\n setRoomVars: () => {\n return wt.room.getRoomById(roomId, pinHash).then(r => {\n var _r$data$classroom;\n\n // setting observers userId's so we can ignore them when creating participant\n room.setObserverIds(r.data.allowedObservers);\n room.setTalkbackIds(r.data.allowedTalkbacks);\n room.setInstructorId((_r$data$classroom = r.data.classroom) === null || _r$data$classroom === void 0 ? void 0 : _r$data$classroom.instructorUserId);\n });\n },\n publishLocal: function () {\n let stream = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let {\n keepAudio = false,\n keepVideo = false\n } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n return room.publishLocal(stream, {\n keepAudio,\n keepVideo\n });\n },\n unpublishLocal: () => {\n return room.unpublishLocal();\n },\n toggleAudio: value => {\n return room.toggleAudio(value);\n },\n toggleVideo: () => {\n return room.toggleVideo();\n },\n setBitrateCap: bitrate => {\n if (isInstructor) {\n return;\n }\n\n return room.sendMessage(room.handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"bitrate\": parseInt(bitrate)\n }\n }).catch(() => null);\n },\n switchChannel: channelId => {\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: channelId,\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n },\n sendSystemMessage: function (action) {\n let value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n let to = arguments.length > 2 ? arguments[2] : undefined;\n let set_master = arguments.length > 3 ? arguments[3] : undefined;\n return room.sendMessage(room.handleId, {\n body: {\n action,\n request: \"message\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n text: JSON.stringify(value),\n ...(to && Array.isArray(to) ? {\n tos: to\n } : {\n to\n }),\n ...(set_master && {\n set_master\n })\n }\n });\n },\n sendChatMessage: (text, to) => {\n return room.sendMessage(room.handleId, {\n body: {\n action: \"chat_message\",\n request: \"message\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n text,\n ...(to && Array.isArray(to) ? {\n tos: to\n } : {\n to\n })\n }\n });\n },\n __requestMuteStatus: function () {\n this.sendSystemMessage('remote_muted_request');\n },\n __sendMuteStatus: function () {\n this.sendSystemMessage('remote_muted', {\n type: 'video',\n value: room.isVideoMuted\n });\n this.sendSystemMessage('remote_muted', {\n type: 'audio',\n value: room.isAudioMuted\n });\n },\n $on: (key, callback, that) => {\n emitter.on(key, callback, that || this);\n room.on(key, callback, that || this);\n },\n $off: (key, callback, that) => {\n emitter.on(key, callback, that || this);\n room.on(key, callback, that || this);\n },\n $clear: function () {\n room.clear();\n emitter.clear();\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (roomSession);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/models/room-session.js?");
|
|
9131
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _modules_wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/wt-utils */ \"./src/modules/wt-utils.js\");\n/* harmony import */ var _modules_wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../modules/wt-emitter */ \"./src/modules/wt-emitter.js\");\n/* harmony import */ var _modules_sync_modules_sync_hls__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../modules/sync-modules/sync-hls */ \"./src/modules/sync-modules/sync-hls.js\");\n/* harmony import */ var _modules_sync_modules_sync_hls_vod__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../modules/sync-modules/sync-hls-vod */ \"./src/modules/sync-modules/sync-hls-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_native_hls_vod__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../modules/sync-modules/sync-native-hls-vod */ \"./src/modules/sync-modules/sync-native-hls-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_native_hls__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../modules/sync-modules/sync-native-hls */ \"./src/modules/sync-modules/sync-native-hls.js\");\n/* harmony import */ var _modules_sync_modules_sync_shaka_dash__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../modules/sync-modules/sync-shaka-dash */ \"./src/modules/sync-modules/sync-shaka-dash.js\");\n/* harmony import */ var _modules_sync_modules_sync_shaka_dash_vod__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../modules/sync-modules/sync-shaka-dash-vod */ \"./src/modules/sync-modules/sync-shaka-dash-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_dash__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../modules/sync-modules/sync-dash */ \"./src/modules/sync-modules/sync-dash.js\");\n/* harmony import */ var _modules_sync_modules_sync_dash_vod__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../modules/sync-modules/sync-dash-vod */ \"./src/modules/sync-modules/sync-dash-vod.js\");\n/* harmony import */ var _modules_sync_modules_sync_doris__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../modules/sync-modules/sync-doris */ \"./src/modules/sync-modules/sync-doris.js\");\n/* harmony import */ var _modules_sync_modules_sync_disabled__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../modules/sync-modules/sync-disabled */ \"./src/modules/sync-modules/sync-disabled.js\");\n/* harmony import */ var _modules_sync_modules_sync_dazn_dash__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../modules/sync-modules/sync-dazn-dash */ \"./src/modules/sync-modules/sync-dazn-dash.js\");\n/* harmony import */ var _modules_sync_modules_sync_universal__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ../modules/sync-modules/sync-universal */ \"./src/modules/sync-modules/sync-universal.js\");\n\n\n\n // SYNCHRONISATION MODULES\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nlet roomSession = function (_ref, room, wt) {\n let {\n roomId,\n pinHash,\n isTalkback,\n isInstructor\n } = _ref;\n let primaryRoomId = roomId;\n let publicCustomEvents = ['changePlayerSource', 'chatMessage', 'userUpdate', 'reconnecting', 'connecting', 'remoteMuted', 'scaling'];\n\n const addEvents = events => {\n publicCustomEvents = [...publicCustomEvents, ...events];\n };\n\n const removeEvents = events => {\n publicCustomEvents = publicCustomEvents.filter(ev => events.indexOf(ev) === -1);\n };\n\n const emitter = Object(_modules_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n\n let alpTimeoutId = null;\n let abortController = null;\n\n let ___; // return object\n\n\n room.on('addLocalParticipant', () => {\n // TODO: this doesnt seem to be fixable until we switch to different type of messaging\n // At some random case we don't get message back if we don't wait\n clearTimeout(alpTimeoutId);\n alpTimeoutId = setTimeout(() => {\n ___.__requestMuteStatus();\n }, 2000);\n });\n room.on('localMuted', _ref2 => {\n let {\n type,\n value\n } = _ref2;\n\n ___.sendSystemMessage('remote_muted', {\n type,\n value\n });\n });\n room.on('data', data => {\n ___.__parseDataEvents(data);\n });\n return ___ = {\n syncModule: null,\n playerInterface: null,\n\n get userId() {\n return room.userId;\n },\n\n get roomId() {\n return roomId;\n },\n\n get sessionId() {\n return room.sessionId;\n },\n\n get constructId() {\n return room.constructId;\n },\n\n destroy: function () {\n clearTimeout(alpTimeoutId);\n this.detachPlayer();\n return room.destroy().finally(() => {\n this.$clear();\n return true;\n });\n },\n iceRestart: function () {\n return room._iceRestart(room.handleId);\n },\n connect: function () {\n var _abortController;\n\n let {\n reactooRoomId = null\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n emitter.emit('connecting', true);\n clearTimeout(alpTimeoutId);\n abortController = new AbortController();\n return Promise.all([wt.room.__joinRoom({\n roomId: reactooRoomId || primaryRoomId,\n pinHash,\n isTalkback,\n isInstructor\n }, (_abortController = abortController) === null || _abortController === void 0 ? void 0 : _abortController.signal), wt.user.getUserSelf()]).then(_ref3 => {\n var _roomData$data;\n\n let [roomData, userData] = _ref3;\n\n // Happens when we reroute user to a different room\n if ((roomData === null || roomData === void 0 ? void 0 : (_roomData$data = roomData.data) === null || _roomData$data === void 0 ? void 0 : _roomData$data.reactooRoomId) !== roomId) {\n roomId = roomData.data.reactooRoomId;\n emitter.emit('changeRoomId', roomId);\n }\n\n return Promise.all([roomData, userData]);\n }).then(_ref4 => {\n let [roomData, userData] = _ref4;\n return Promise.all([roomData, userData, this.setRoomVars()]);\n }).then(_ref5 => {\n let [roomData, userData, _] = _ref5;\n return Promise.all([roomData, userData, room.connect(roomData.data.roomId, roomData.data.pin, roomData.data.href, roomData.data.iceServers, roomData.data.accessToken, roomData.data.display, roomData.data.userId, roomData.data.webrtcVersion, roomData.data.bitrate ? parseInt(roomData.data.bitrate) : 0, roomData.data.recordingFilename)]);\n }).finally(() => {\n emitter.emit('connecting', false);\n });\n },\n disconnect: function (dontWaitForResponses) {\n var _abortController2, _abortController2$abo;\n\n clearTimeout(alpTimeoutId);\n (_abortController2 = abortController) === null || _abortController2 === void 0 ? void 0 : (_abortController2$abo = _abortController2.abort) === null || _abortController2$abo === void 0 ? void 0 : _abortController2$abo.call(_abortController2);\n return room.disconnect(dontWaitForResponses);\n },\n //TODO: refactor restart method\n restart: function () {\n var _handle$webrtcStuff;\n\n let {\n isObserver = false,\n reactooRoomId = null\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n emitter.emit('reconnecting', true);\n room.isRestarting = true;\n let wasPublished = room.isPublished;\n\n let handle = room._getHandle(room.handleId);\n\n let stream = null;\n\n if (handle !== null && handle !== void 0 && (_handle$webrtcStuff = handle.webrtcStuff) !== null && _handle$webrtcStuff !== void 0 && _handle$webrtcStuff.stream && wasPublished) {\n stream = handle.webrtcStuff.stream;\n }\n\n return this.disconnect().then(() => Object(_modules_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(1000)) //TODO: remove 1000ms wait by waiting for proper events from janus\n .then(() => this.connect({\n reactooRoomId\n })).then(() => {\n if (isObserver) {\n return this.publishLocal(null);\n } else if (stream) {\n return this.publishLocal(stream);\n } else return Promise.resolve();\n }).then(() => {\n room.isRestarting = false;\n emitter.emit('reconnecting', false);\n return 1;\n }).catch(error => {\n room.isRestarting = false;\n emitter.emit('reconnecting', false);\n emitter.emit('error', {\n type: 'error',\n id: 26,\n message: 'reconnecting failed',\n data: error\n });\n return Promise.reject(0);\n });\n },\n getStats: function () {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n return room._getStats(type);\n },\n getRoomParticipants: function () {\n return room._participants;\n },\n __parseDataEvents: function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'message') {\n if (msg.action === 'pending_shutdown' || msg.action === 'shutting_down') {\n emitter.emit('scaling');\n this.restart(); // current videoroom was reassigned to a different WebRTC instance\n } else if (msg.action === 'force_restart') {\n this.restart();\n } else if (msg.action === 'user_update_displayname' || msg.action === 'user_update_avatar' || msg.action === 'user_update_customattributes' || msg.action === 'user_update_privateattributes') {\n emitter.emit('userUpdate', msg.text);\n } else if (msg.action === 'observer_connecting' || msg.action === 'talkback_connecting' || msg.action === 'instructor_connecting') {\n this.setRoomVars().catch(e => {\n room._log('Setting observers failed, this will cause issues', e);\n });\n } else if (msg.action === 'bitrate_changed') {\n this.setBitrateCap(msg.text);\n } else if (msg.user_action === 'chat_message') {\n emitter.emit('chatMessage', msg);\n } else if (msg.user_action === 'remote_muted') {\n if (msg.from !== room.userId) {\n var _decodeJanusDisplay;\n\n emitter.emit('remoteMuted', {\n userId: (_decodeJanusDisplay = Object(_modules_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"decodeJanusDisplay\"])(msg.from)) === null || _decodeJanusDisplay === void 0 ? void 0 : _decodeJanusDisplay.userId,\n ...(msg.text && JSON.parse(msg.text))\n });\n }\n } else if (msg.user_action === 'remote_muted_request') {\n if (msg.from !== room.userId) {\n this.__sendMuteStatus();\n }\n }\n }\n },\n renderPlayer: function (playerWrapper, fullscreenElement, roomId) {\n try {\n this.syncModule = Object(_modules_sync_modules_sync_universal__WEBPACK_IMPORTED_MODULE_13__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n this.playerInterface = wt.__privates.playerFactory(playerWrapper, fullscreenElement, this.syncModule.getHandlers(), {\n roomId\n });\n this.syncModule.initialize({\n playerInterface: this.playerInterface\n });\n return true;\n } catch (e) {\n return false;\n }\n },\n attachPlayer: function (type, inputs) {\n this.detachPlayer();\n\n if (type === 'hlsjs') {\n this.syncModule = Object(_modules_sync_modules_sync_hls__WEBPACK_IMPORTED_MODULE_2__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'hlsjs-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_hls_vod__WEBPACK_IMPORTED_MODULE_3__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'hlsnative-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_native_hls_vod__WEBPACK_IMPORTED_MODULE_4__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'hlsnative') {\n this.syncModule = Object(_modules_sync_modules_sync_native_hls__WEBPACK_IMPORTED_MODULE_5__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'shaka-dash-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_shaka_dash_vod__WEBPACK_IMPORTED_MODULE_7__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'shaka-dash') {\n this.syncModule = Object(_modules_sync_modules_sync_shaka_dash__WEBPACK_IMPORTED_MODULE_6__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'dashjs') {\n this.syncModule = Object(_modules_sync_modules_sync_dash__WEBPACK_IMPORTED_MODULE_8__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'dashjs-vod') {\n this.syncModule = Object(_modules_sync_modules_sync_dash_vod__WEBPACK_IMPORTED_MODULE_9__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'doris') {\n this.syncModule = Object(_modules_sync_modules_sync_doris__WEBPACK_IMPORTED_MODULE_10__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'dazn-dash') {\n this.syncModule = Object(_modules_sync_modules_sync_dazn_dash__WEBPACK_IMPORTED_MODULE_12__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else if (type === 'disabled') {\n this.syncModule = Object(_modules_sync_modules_sync_disabled__WEBPACK_IMPORTED_MODULE_11__[\"default\"])({\n room,\n wt,\n roomSession: this,\n emitter\n });\n\n if (this.syncModule.__events) {\n addEvents(this.syncModule.__events);\n }\n\n this.syncModule.initialize(inputs);\n } else {\n room._log('Synchronisation type not recognised');\n }\n },\n detachPlayer: function () {\n if (this.syncModule) {\n this.playerInterface = null;\n this.syncModule.destroy();\n\n if (this.syncModule.__events) {\n removeEvents(this.syncModule.__events);\n }\n\n this.syncModule = null;\n }\n },\n setRoomVars: () => {\n var _abortController3;\n\n return wt.room.getRoomById(roomId, pinHash, undefined, false, (_abortController3 = abortController) === null || _abortController3 === void 0 ? void 0 : _abortController3.signal).then(r => {\n var _r$data$classroom, _r$data$videowall;\n\n // setting observers userId's so we can ignore them when creating participant\n room.setRoomType(r.data.type);\n room.setObserverIds(r.data.allowedObservers);\n room.setTalkbackIds(r.data.allowedTalkbacks);\n room.setInstructorId(((_r$data$classroom = r.data.classroom) === null || _r$data$classroom === void 0 ? void 0 : _r$data$classroom.instructorUserId) || ((_r$data$videowall = r.data.videowall) === null || _r$data$videowall === void 0 ? void 0 : _r$data$videowall.hostUserId));\n });\n },\n publishLocal: function () {\n let stream = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let {\n keepAudio = false,\n keepVideo = false\n } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n return room.publishLocal(stream, {\n keepAudio,\n keepVideo\n });\n },\n unpublishLocal: () => {\n return room.unpublishLocal();\n },\n toggleAudio: value => {\n return room.toggleAudio(value);\n },\n toggleVideo: () => {\n return room.toggleVideo();\n },\n setBitrateCap: bitrate => {\n if (isInstructor) {\n return;\n }\n\n return room.sendMessage(room.handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"bitrate\": parseInt(bitrate)\n }\n }).catch(() => null);\n },\n switchChannel: channelId => {\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: channelId,\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n },\n sendSystemMessage: function (action) {\n let value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n let to = arguments.length > 2 ? arguments[2] : undefined;\n let set_master = arguments.length > 3 ? arguments[3] : undefined;\n return room.sendMessage(room.handleId, {\n body: {\n action,\n request: \"message\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n text: JSON.stringify(value),\n ...(to && Array.isArray(to) ? {\n tos: to\n } : {\n to\n }),\n ...(set_master && {\n set_master\n })\n }\n });\n },\n sendChatMessage: (text, to) => {\n return room.sendMessage(room.handleId, {\n body: {\n action: \"chat_message\",\n request: \"message\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n text,\n ...(to && Array.isArray(to) ? {\n tos: to\n } : {\n to\n })\n }\n });\n },\n __requestMuteStatus: function () {\n this.sendSystemMessage('remote_muted_request');\n },\n __sendMuteStatus: function () {\n this.sendSystemMessage('remote_muted', {\n type: 'video',\n value: room.isVideoMuted\n });\n this.sendSystemMessage('remote_muted', {\n type: 'audio',\n value: room.isAudioMuted\n });\n },\n $on: (key, callback, that) => {\n emitter.on(key, callback, that || this);\n room.on(key, callback, that || this);\n },\n $off: (key, callback, that) => {\n emitter.on(key, callback, that || this);\n room.on(key, callback, that || this);\n },\n $clear: function () {\n room.clear();\n emitter.clear();\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (roomSession);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/models/room-session.js?");
|
|
9132
9132
|
|
|
9133
9133
|
/***/ }),
|
|
9134
9134
|
|
|
@@ -9140,7 +9140,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _mod
|
|
|
9140
9140
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9141
9141
|
|
|
9142
9142
|
"use strict";
|
|
9143
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _room_session__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./room-session */ \"./src/models/room-session.js\");\n/* harmony import */ var _streaming_session__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./streaming-session */ \"./src/models/streaming-session.js\");\n\n\n\n\n\nlet room = function () {\n var _this = this;\n\n let roomSessions = [];\n\n let setExitListeners = () => {\n window.addEventListener('pagehide', event => {\n if (!event.persisted) {\n this.room.destroySessions();\n }\n });\n window.addEventListener('beforeunload', () => {\n this.room.destroySessions();\n });\n };\n\n setExitListeners();\n return {\n //TODO: new model\n integration: function () {\n let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.system.integration({\n type: _this.__instanceType\n }, {\n requestBody: { ...data\n },\n requestInterceptor: req => {\n if (!req.headers) {\n req.headers = {};\n }\n\n req.headers['Authorization'] = 'Bearer ' + localStorage.getItem('rwt_idToken');\n return req;\n }\n }));\n },\n //TODO: new model\n getAnalytics: function () {\n let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.system.getAnalytics({\n instanceType: _this.__instanceType,\n ...data\n }));\n },\n sendChatMessage: _ref => {\n let {\n roomId,\n message,\n options,\n senderUserId\n } = _ref;\n return this.__privates.auth.__client.then(client => client.apis.wt.sendRoomMessage({}, {\n requestBody: {\n roomId,\n message,\n options,\n senderUserId\n }\n }));\n },\n createRoom: function () {\n let {\n title,\n description,\n isPublic,\n isListed,\n isStudioLayout,\n allowedParticipants,\n wtChannelId,\n isHd,\n disableSync,\n reduceRoomControls,\n muteParticipantOnJoin,\n hasStudioChat,\n maxParticipants,\n customAttributes\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.wt.createRoom({}, {\n requestBody: {\n title,\n description,\n isPublic,\n isListed,\n isStudioLayout,\n instanceType: _this.__instanceType,\n allowedParticipants,\n wtChannelId,\n isHd,\n disableSync,\n reduceRoomControls,\n muteParticipantOnJoin,\n hasStudioChat,\n maxParticipants,\n customAttributes\n }\n }));\n },\n updateRoom: function () {\n let {\n roomId,\n title,\n description,\n isPublic,\n isListed,\n allowedParticipants,\n recordings,\n slug,\n password,\n maxParticipants,\n setInstanceType = false,\n wtChannelId,\n isHd,\n isStudioLayout,\n hasStudioChat,\n reduceRoomControls,\n muteParticipantOnJoin,\n disableSync,\n defaultRegion,\n customAttributes,\n dotAttribute\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n let _da = dotAttribute ? Array.isArray(dotAttribute) ? dotAttribute.reduce((p, cv) => (p[cv.name] = cv.value) && p || p, {}) : {\n [dotAttribute.name]: dotAttribute.value\n } : {};\n\n return _this.__privates.auth.__client.then(client => client.apis.wt.updateRoom({\n id: roomId\n }, {\n requestBody: { ..._da,\n title,\n description,\n slug,\n password,\n isPublic,\n isListed,\n isHd,\n isStudioLayout,\n hasStudioChat,\n maxParticipants,\n reduceRoomControls,\n muteParticipantOnJoin,\n disableSync,\n defaultRegion,\n customAttributes,\n wtChannelId,\n ...(setInstanceType && {\n instanceType: setInstanceType === true ? _this.__instanceType : setInstanceType\n }),\n ...(allowedParticipants && {\n allowedParticipants\n }),\n ...(recordings && {\n recordings\n })\n }\n }));\n },\n deleteRoom: roomId => {\n return this.__privates.auth.__client.then(client => client.apis.wt.deleteRoom({\n id: roomId\n }));\n },\n leaveRoom: roomId => {\n return this.__privates.auth.__client.then(client => client.apis.wt.leave({\n roomId\n }));\n },\n updateLayout: _ref2 => {\n let {\n roomId,\n operation,\n name,\n attributeName,\n value,\n target\n } = _ref2;\n //operation : clearLayouts, setLayout, setLayouts, unsetLayout, setName, setTarget, setStyle, setAttribute, unsetAttribute,'sendMessage'\n return this.__privates.auth.__client.then(client => client.apis.wt.updateLayout({}, {\n requestBody: {\n roomId,\n operation,\n name,\n attributeName,\n value,\n target\n }\n }));\n },\n getInviteUrl: (roomId, domain, url) => {\n return this.__privates.auth.__client.then(client => client.apis.wt.invite({}, {\n requestBody: {\n roomId,\n domain: domain || location.host,\n url: url || location.href\n }\n }));\n },\n getRoomChatList: function () {\n let {\n roomId,\n type = \"normal\",\n size = 50,\n startKey = null\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let apiParams = {\n roomId,\n type,\n size,\n ...(startKey && {\n startKey\n })\n };\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoomChatList(apiParams));\n },\n getRoomsList: function () {\n let {\n type = 'participant',\n activeOnly = null,\n instanceType = _this.__instanceType,\n size = 20,\n startKey = null,\n includeWtEventModels = false,\n demo = false,\n viewType = 'list',\n wtChannelId = undefined\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let apiParams = {\n type,\n instanceType,\n size,\n includeWtEventModels,\n demo,\n viewType,\n ...(wtChannelId && {\n wtChannelId\n }),\n ...(activeOnly && {\n activeOnly\n }),\n ...(startKey && {\n startKey\n })\n };\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoomList(apiParams));\n },\n setUser: _ref3 => {\n let {\n messageId,\n userId,\n roomId,\n flag,\n timestamp,\n option\n } = _ref3;\n //leave, kick, ban, unban, approve, report\n return this.__privates.auth.__client.then(client => client.apis.wt.setUser({\n messageId,\n userId,\n roomId,\n flag,\n timestamp,\n option\n }));\n },\n getRoomById: function (id) {\n let pinHash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;\n let showPublic = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n let demo = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoom({\n id,\n pinHash,\n \"public\": showPublic,\n demo\n }));\n },\n getRoomBySlug: function (slug) {\n let password = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;\n let showPublic = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n let demo = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoom({\n slug,\n password,\n \"public\": showPublic,\n demo\n }));\n },\n roomRecorder: function () {\n let {\n roomId,\n operation = 'get',\n config\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.wt.recordRoom({}, {\n requestBody: {\n roomId,\n operation,\n config\n }\n }));\n },\n __joinRoom: function () {\n let {\n roomId,\n pinHash,\n instanceType = _this.__instanceType,\n isTalkback = undefined,\n isMonitor = undefined,\n isInstructor = undefined,\n customBearer = undefined\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.wt.join({\n roomId,\n pinHash,\n instanceType,\n isTalkback,\n isMonitor,\n isInstructor,\n platform: _this.browser === 'firefox' ? 'web-firefox' : 'web'\n }, { ...(customBearer ? {\n requestInterceptor: req => {\n if (!req.headers) {\n req.headers = {};\n }\n\n req.headers['Authorization'] = customBearer;\n return req;\n }\n } : {})\n }));\n },\n isSafariSupported: () => {\n return this.__privates.room.safariVp8;\n },\n isWebrtcSupported: () => {\n return this.__privates.room.isWebrtcSupported;\n },\n createStreamingSession: function () {\n let {\n constructId,\n roomId,\n pinHash,\n streamId,\n href,\n iceServers,\n accessToken\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.room.whenInitialized.then(lib => _streaming_session__WEBPACK_IMPORTED_MODULE_1__[\"default\"].call(_this, {\n roomId,\n pinHash,\n streamId,\n href,\n iceServers,\n accessToken\n }, lib.createSession(constructId, 'streaming'), _this)).then(newSession => {\n let existingSessionIndex = roomSessions.findIndex(session => session.constructId === newSession.constructId);\n\n if (existingSessionIndex > -1) {\n return _this.room.destroySession(newSession.constructId).then(() => {\n roomSessions.push(newSession);\n return newSession;\n });\n } else {\n roomSessions.push(newSession);\n return newSession;\n }\n });\n },\n createSession: _ref4 => {\n let {\n constructId,\n roomId,\n pinHash,\n isTalkback,\n isMonitor,\n isInstructor,\n options\n } = _ref4;\n return this.__privates.room.whenInitialized.then(lib => _room_session__WEBPACK_IMPORTED_MODULE_0__[\"default\"].call(this, {\n roomId,\n pinHash,\n isTalkback,\n isMonitor,\n isInstructor\n }, lib.createSession(constructId, 'reactooroom', options), this)).then(newSession => {\n let existingSessionIndex = roomSessions.findIndex(session => session.constructId === newSession.constructId);\n\n if (existingSessionIndex > -1) {\n return this.room.destroySession(newSession.constructId).then(() => {\n roomSessions.push(newSession);\n return newSession;\n });\n } else {\n roomSessions.push(newSession);\n return newSession;\n }\n });\n },\n getSessions: () => {\n return roomSessions;\n },\n getSessionByConstructId: constructId => {\n return roomSessions.find(session => session.constructId === constructId) || null;\n },\n getSessionBySessionId: sessionId => {\n return roomSessions.find(session => session.sessionId === sessionId) || null;\n },\n getSessionByRoomId: roomId => {\n return roomSessions.find(session => session.roomId === roomId) || null;\n },\n getSessionByUserId: userId => {\n return roomSessions.find(session => session.userId === userId) || null;\n },\n getSessionByStreamId: streamId => {\n return roomSessions.find(session => session.streamId === streamId) || null;\n },\n destroySession: constructId => {\n let sessionIndex = roomSessions.findIndex(session => session.constructId === constructId);\n\n if (sessionIndex > -1) {\n return roomSessions[sessionIndex].destroy().finally(() => {\n roomSessions.splice(sessionIndex, 1);\n return true;\n });\n }\n\n return Promise.resolve();\n },\n destroySessionBySessionId: sessionId => {\n let sessionIndex = roomSessions.findIndex(session => session.sessionId === sessionId);\n\n if (sessionIndex > -1) {\n return roomSessions[sessionIndex].destroy().finally(() => {\n roomSessions.splice(sessionIndex, 1);\n return true;\n });\n }\n\n return Promise.resolve();\n },\n destroySessions: () => {\n roomSessions.forEach(session => session.destroy().catch(e => this.log(e)));\n roomSessions.length = 0;\n },\n queue: function () {\n let {\n roomId,\n operation,\n userId\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //Enum: \"status\" \"join\" \"approve\" \"reject\" \"block\" \"unblock\"\n return _this.__privates.auth.__client.then(client => client.apis.wt.queue({}, {\n requestBody: {\n roomId,\n operation,\n userId\n }\n }));\n },\n getDefaultRegions: () => {\n return this.__privates.auth.__client.then(client => {\n var _client$spec, _client$spec$componen, _client$spec$componen2, _client$spec$componen3, _client$spec$componen4, _client$spec$componen5;\n\n return [...(((_client$spec = client.spec) === null || _client$spec === void 0 ? void 0 : (_client$spec$componen = _client$spec.components) === null || _client$spec$componen === void 0 ? void 0 : (_client$spec$componen2 = _client$spec$componen.schemas) === null || _client$spec$componen2 === void 0 ? void 0 : (_client$spec$componen3 = _client$spec$componen2.WtRoom) === null || _client$spec$componen3 === void 0 ? void 0 : (_client$spec$componen4 = _client$spec$componen3.properties) === null || _client$spec$componen4 === void 0 ? void 0 : (_client$spec$componen5 = _client$spec$componen4.defaultRegion) === null || _client$spec$componen5 === void 0 ? void 0 : _client$spec$componen5.enum) || [''])];\n });\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (room);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/models/room.js?");
|
|
9143
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _room_session__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./room-session */ \"./src/models/room-session.js\");\n/* harmony import */ var _streaming_session__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./streaming-session */ \"./src/models/streaming-session.js\");\n\n\n\n\n\nlet room = function () {\n var _this = this;\n\n let roomSessions = [];\n\n let setExitListeners = () => {\n window.addEventListener('pagehide', event => {\n if (!event.persisted) {\n this.room.destroySessions();\n }\n });\n window.addEventListener('beforeunload', () => {\n this.room.destroySessions();\n });\n };\n\n setExitListeners();\n return {\n //TODO: new model\n integration: function () {\n let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.system.integration({\n type: _this.__instanceType\n }, {\n requestBody: { ...data\n },\n requestInterceptor: req => {\n if (!req.headers) {\n req.headers = {};\n }\n\n req.headers['Authorization'] = 'Bearer ' + localStorage.getItem('rwt_idToken');\n return req;\n }\n }));\n },\n //TODO: new model\n getAnalytics: function () {\n let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.system.getAnalytics({\n instanceType: _this.__instanceType,\n ...data\n }));\n },\n sendChatMessage: _ref => {\n let {\n roomId,\n message,\n options,\n senderUserId\n } = _ref;\n return this.__privates.auth.__client.then(client => client.apis.wt.sendRoomMessage({}, {\n requestBody: {\n roomId,\n message,\n options,\n senderUserId\n }\n }));\n },\n createRoom: function () {\n let {\n title,\n description,\n isPublic,\n isListed,\n isStudioLayout,\n allowedParticipants,\n wtChannelId,\n isHd,\n disableSync,\n reduceRoomControls,\n muteParticipantOnJoin,\n hasStudioChat,\n maxParticipants,\n customAttributes\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.wt.createRoom({}, {\n requestBody: {\n title,\n description,\n isPublic,\n isListed,\n isStudioLayout,\n instanceType: _this.__instanceType,\n allowedParticipants,\n wtChannelId,\n isHd,\n disableSync,\n reduceRoomControls,\n muteParticipantOnJoin,\n hasStudioChat,\n maxParticipants,\n customAttributes\n }\n }));\n },\n updateRoom: function () {\n let {\n roomId,\n title,\n description,\n isPublic,\n isListed,\n allowedParticipants,\n recordings,\n slug,\n password,\n maxParticipants,\n setInstanceType = false,\n wtChannelId,\n isHd,\n isStudioLayout,\n hasStudioChat,\n reduceRoomControls,\n muteParticipantOnJoin,\n disableSync,\n defaultRegion,\n customAttributes,\n dotAttribute\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n let _da = dotAttribute ? Array.isArray(dotAttribute) ? dotAttribute.reduce((p, cv) => (p[cv.name] = cv.value) && p || p, {}) : {\n [dotAttribute.name]: dotAttribute.value\n } : {};\n\n return _this.__privates.auth.__client.then(client => client.apis.wt.updateRoom({\n id: roomId\n }, {\n requestBody: { ..._da,\n title,\n description,\n slug,\n password,\n isPublic,\n isListed,\n isHd,\n isStudioLayout,\n hasStudioChat,\n maxParticipants,\n reduceRoomControls,\n muteParticipantOnJoin,\n disableSync,\n defaultRegion,\n customAttributes,\n wtChannelId,\n ...(setInstanceType && {\n instanceType: setInstanceType === true ? _this.__instanceType : setInstanceType\n }),\n ...(allowedParticipants && {\n allowedParticipants\n }),\n ...(recordings && {\n recordings\n })\n }\n }));\n },\n deleteRoom: roomId => {\n return this.__privates.auth.__client.then(client => client.apis.wt.deleteRoom({\n id: roomId\n }));\n },\n leaveRoom: roomId => {\n return this.__privates.auth.__client.then(client => client.apis.wt.leave({\n roomId\n }));\n },\n updateLayout: _ref2 => {\n let {\n roomId,\n operation,\n name,\n attributeName,\n value,\n target\n } = _ref2;\n //operation : clearLayouts, setLayout, setLayouts, unsetLayout, setName, setTarget, setStyle, setAttribute, unsetAttribute,'sendMessage'\n return this.__privates.auth.__client.then(client => client.apis.wt.updateLayout({}, {\n requestBody: {\n roomId,\n operation,\n name,\n attributeName,\n value,\n target\n }\n }));\n },\n getInviteUrl: (roomId, domain, url) => {\n return this.__privates.auth.__client.then(client => client.apis.wt.invite({}, {\n requestBody: {\n roomId,\n domain: domain || location.host,\n url: url || location.href\n }\n }));\n },\n getRoomChatList: function () {\n let {\n roomId,\n type = \"normal\",\n size = 50,\n startKey = null\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let apiParams = {\n roomId,\n type,\n size,\n ...(startKey && {\n startKey\n })\n };\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoomChatList(apiParams));\n },\n getRoomsList: function () {\n let {\n type = 'participant',\n activeOnly = null,\n instanceType = _this.__instanceType,\n size = 20,\n startKey = null,\n includeWtEventModels = false,\n demo = false,\n viewType = 'list',\n wtChannelId = undefined\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let apiParams = {\n type,\n instanceType,\n size,\n includeWtEventModels,\n demo,\n viewType,\n ...(wtChannelId && {\n wtChannelId\n }),\n ...(activeOnly && {\n activeOnly\n }),\n ...(startKey && {\n startKey\n })\n };\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoomList(apiParams));\n },\n setUser: _ref3 => {\n let {\n messageId,\n userId,\n roomId,\n flag,\n timestamp,\n option\n } = _ref3;\n //leave, kick, ban, unban, approve, report\n return this.__privates.auth.__client.then(client => client.apis.wt.setUser({\n messageId,\n userId,\n roomId,\n flag,\n timestamp,\n option\n }));\n },\n getRoomById: function (id) {\n let pinHash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;\n let showPublic = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n let demo = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n let signal = arguments.length > 4 ? arguments[4] : undefined;\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoom({\n id,\n pinHash,\n \"public\": showPublic,\n demo\n }, { ...(signal ? {\n requestInterceptor: req => {\n req.signal = signal;\n return req;\n }\n } : {})\n }));\n },\n getRoomBySlug: function (slug) {\n let password = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;\n let showPublic = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n let demo = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n return _this.__privates.auth.__client.then(client => client.apis.wt.getRoom({\n slug,\n password,\n \"public\": showPublic,\n demo\n }));\n },\n roomRecorder: function () {\n let {\n roomId,\n operation = 'get',\n config\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.auth.__client.then(client => client.apis.wt.recordRoom({}, {\n requestBody: {\n roomId,\n operation,\n config\n }\n }));\n },\n __joinRoom: function () {\n let {\n roomId,\n pinHash,\n instanceType = _this.__instanceType,\n isTalkback = undefined,\n isInstructor = undefined,\n customBearer = undefined\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let signal = arguments.length > 1 ? arguments[1] : undefined;\n return _this.__privates.auth.__client.then(client => client.apis.wt.join({\n roomId,\n pinHash,\n instanceType,\n isTalkback,\n isInstructor,\n platform: _this.browser === 'firefox' ? 'web-firefox' : 'web'\n }, { ...(signal ? {\n requestInterceptor: req => {\n req.signal = signal;\n return req;\n }\n } : {})\n }));\n },\n isSafariSupported: () => {\n return this.__privates.room.safariVp8;\n },\n isWebrtcSupported: () => {\n return this.__privates.room.isWebrtcSupported;\n },\n createStreamingSession: function () {\n let {\n constructId,\n roomId,\n pinHash,\n streamId,\n href,\n iceServers,\n accessToken\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n return _this.__privates.room.whenInitialized.then(lib => _streaming_session__WEBPACK_IMPORTED_MODULE_1__[\"default\"].call(_this, {\n roomId,\n pinHash,\n streamId,\n href,\n iceServers,\n accessToken\n }, lib.createSession(constructId, 'streaming'), _this)).then(newSession => {\n let existingSessionIndex = roomSessions.findIndex(session => session.constructId === newSession.constructId);\n\n if (existingSessionIndex > -1) {\n return _this.room.destroySession(newSession.constructId).then(() => {\n roomSessions.push(newSession);\n return newSession;\n });\n } else {\n roomSessions.push(newSession);\n return newSession;\n }\n });\n },\n createSession: _ref4 => {\n let {\n constructId,\n roomId,\n pinHash,\n isTalkback,\n isMonitor,\n isInstructor,\n options\n } = _ref4;\n return this.__privates.room.whenInitialized.then(lib => _room_session__WEBPACK_IMPORTED_MODULE_0__[\"default\"].call(this, {\n roomId,\n pinHash,\n isTalkback,\n isMonitor,\n isInstructor\n }, lib.createSession(constructId, 'reactooroom', options), this)).then(newSession => {\n let existingSessionIndex = roomSessions.findIndex(session => session.constructId === newSession.constructId);\n\n if (existingSessionIndex > -1) {\n return this.room.destroySession(newSession.constructId).then(() => {\n roomSessions.push(newSession);\n return newSession;\n });\n } else {\n roomSessions.push(newSession);\n return newSession;\n }\n });\n },\n getSessions: () => {\n return roomSessions;\n },\n getSessionByConstructId: constructId => {\n return roomSessions.find(session => session.constructId === constructId) || null;\n },\n getSessionBySessionId: sessionId => {\n return roomSessions.find(session => session.sessionId === sessionId) || null;\n },\n getSessionByRoomId: roomId => {\n return roomSessions.find(session => session.roomId === roomId) || null;\n },\n getSessionByUserId: userId => {\n return roomSessions.find(session => session.userId === userId) || null;\n },\n getSessionByStreamId: streamId => {\n return roomSessions.find(session => session.streamId === streamId) || null;\n },\n destroySession: constructId => {\n let sessionIndex = roomSessions.findIndex(session => session.constructId === constructId);\n\n if (sessionIndex > -1) {\n return roomSessions[sessionIndex].destroy().finally(() => {\n roomSessions.splice(sessionIndex, 1);\n return true;\n });\n }\n\n return Promise.resolve();\n },\n destroySessionBySessionId: sessionId => {\n let sessionIndex = roomSessions.findIndex(session => session.sessionId === sessionId);\n\n if (sessionIndex > -1) {\n return roomSessions[sessionIndex].destroy().finally(() => {\n roomSessions.splice(sessionIndex, 1);\n return true;\n });\n }\n\n return Promise.resolve();\n },\n destroySessions: () => {\n roomSessions.forEach(session => session.destroy().catch(e => this.log(e)));\n roomSessions.length = 0;\n },\n queue: function () {\n let {\n roomId,\n operation,\n userId\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //Enum: \"status\" \"join\" \"approve\" \"reject\" \"block\" \"unblock\"\n return _this.__privates.auth.__client.then(client => client.apis.wt.queue({}, {\n requestBody: {\n roomId,\n operation,\n userId\n }\n }));\n },\n getDefaultRegions: () => {\n return this.__privates.auth.__client.then(client => {\n var _client$spec, _client$spec$componen, _client$spec$componen2, _client$spec$componen3, _client$spec$componen4, _client$spec$componen5;\n\n return [...(((_client$spec = client.spec) === null || _client$spec === void 0 ? void 0 : (_client$spec$componen = _client$spec.components) === null || _client$spec$componen === void 0 ? void 0 : (_client$spec$componen2 = _client$spec$componen.schemas) === null || _client$spec$componen2 === void 0 ? void 0 : (_client$spec$componen3 = _client$spec$componen2.WtRoom) === null || _client$spec$componen3 === void 0 ? void 0 : (_client$spec$componen4 = _client$spec$componen3.properties) === null || _client$spec$componen4 === void 0 ? void 0 : (_client$spec$componen5 = _client$spec$componen4.defaultRegion) === null || _client$spec$componen5 === void 0 ? void 0 : _client$spec$componen5.enum) || [''])];\n });\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (room);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/models/room.js?");
|
|
9144
9144
|
|
|
9145
9145
|
/***/ }),
|
|
9146
9146
|
|
|
@@ -9200,7 +9200,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n\n\n/* harmony default export
|
|
|
9200
9200
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9201
9201
|
|
|
9202
9202
|
"use strict";
|
|
9203
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodDashJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n restartSyncLoop();\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const buffering = event => {\n if (event.mediaType === 'video' || event.mediaType === 'audio') {\n if (event.state === 'bufferStalled') {\n handleStalledWaiting();\n }\n }\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n dashInstance,\n libraryInstance,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = dashInstance || libraryInstance;\n _videoElement = _libraryInstance.getVideoElement();\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_libraryInstance) {\n console.log('No player instance');\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_libraryInstance && _videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _libraryInstance.on('bufferStateChanged', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('bufferStateChanged', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _libraryInstance = null;\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodDashJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-dash-vod.js?");
|
|
9203
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodDashJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n restartSyncLoop();\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const buffering = event => {\n if (event.mediaType === 'video' || event.mediaType === 'audio') {\n if (event.state === 'bufferStalled') {\n handleStalledWaiting();\n }\n }\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n dashInstance,\n libraryInstance,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = dashInstance || libraryInstance;\n _videoElement = _libraryInstance.getVideoElement();\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_libraryInstance) {\n console.log('No player instance');\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_libraryInstance && _videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _libraryInstance.on('bufferStateChanged', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('bufferStateChanged', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _libraryInstance = null;\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodDashJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-dash-vod.js?");
|
|
9204
9204
|
|
|
9205
9205
|
/***/ }),
|
|
9206
9206
|
|
|
@@ -9212,7 +9212,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9212
9212
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9213
9213
|
|
|
9214
9214
|
"use strict";
|
|
9215
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDashJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const buffering = event => {\n if (event.mediaType === 'video' || event.mediaType === 'audio') {\n if (event.state === 'bufferStalled') {\n handleStalledWaiting();\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n realPosition: position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPosition: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n dashInstance,\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = dashInstance || libraryInstance; // backwards comp;\n\n _videoElement = _libraryInstance.getVideoElement();\n\n if (!_libraryInstance) {\n room._log('No dash player instance!');\n\n return;\n }\n\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _libraryInstance.on('bufferStateChanged', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('bufferStateChanged', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDashJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-dash.js?");
|
|
9215
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDashJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const buffering = event => {\n if (event.mediaType === 'video' || event.mediaType === 'audio') {\n if (event.state === 'bufferStalled') {\n handleStalledWaiting();\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n realPosition: position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPosition: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n dashInstance,\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = dashInstance || libraryInstance; // backwards comp;\n\n _videoElement = _libraryInstance.getVideoElement();\n\n if (!_libraryInstance) {\n room._log('No dash player instance!');\n\n return;\n }\n\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _libraryInstance.on('bufferStateChanged', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('bufferStateChanged', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDashJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-dash.js?");
|
|
9216
9216
|
|
|
9217
9217
|
/***/ }),
|
|
9218
9218
|
|
|
@@ -9224,7 +9224,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9224
9224
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9225
9225
|
|
|
9226
9226
|
"use strict";
|
|
9227
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDaznDash = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n realPostion: position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPostion: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPostion: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPostion: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n }; // const seekTo = (time) => {\n // return new Promise((resolve, reject) => {\n // if(_videoElement.currentTime !== time) {\n // let __ = Date.now();\n // isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n // _videoElement.addEventListener('playing', () => {\n // room._log(`It took the player ${(Date.now() - __) / 1000} seconds to seek `);\n // isProgrammaticallySeeked = false;\n // resolve();\n // }, {once:true});\n // _videoElement.currentTime = time;\n // } else resolve()\n // });\n // };\n\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = libraryInstance;\n _videoElement = _libraryInstance.videoElement;\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _libraryInstance.addEventListener('buffer', handleStalledWaiting);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.removeEventListener('buffer', handleStalledWaiting);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDaznDash);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-dazn-dash.js?");
|
|
9227
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDaznDash = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n realPostion: position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPostion: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPostion: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPostion: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n }; // const seekTo = (time) => {\n // return new Promise((resolve, reject) => {\n // if(_videoElement.currentTime !== time) {\n // let __ = Date.now();\n // isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n // _videoElement.addEventListener('playing', () => {\n // room._log(`It took the player ${(Date.now() - __) / 1000} seconds to seek `);\n // isProgrammaticallySeeked = false;\n // resolve();\n // }, {once:true});\n // _videoElement.currentTime = time;\n // } else resolve()\n // });\n // };\n\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = libraryInstance;\n _videoElement = _libraryInstance.videoElement;\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _libraryInstance.addEventListener('buffer', handleStalledWaiting);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.removeEventListener('buffer', handleStalledWaiting);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDaznDash);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-dazn-dash.js?");
|
|
9228
9228
|
|
|
9229
9229
|
/***/ }),
|
|
9230
9230
|
|
|
@@ -9248,7 +9248,7 @@ eval("__webpack_require__.r(__webpack_exports__);\nconst syncDisabled = function
|
|
|
9248
9248
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9249
9249
|
|
|
9250
9250
|
"use strict";
|
|
9251
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDoris = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n act,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n\n if (position && Math.abs(position) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekDoris(position, act).then(() => {\n const seekDuration = Date.now() - syncStartTime;\n const {\n position\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos + seekDuration, syncData.ping);\n const syncPrecision = Math.abs(position);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n var position;\n return (position = _libraryInstance.getCurrentDate()) === null || position === void 0 ? 0 : position.getTime();\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn, fragmentPos) {\n let ping = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n var _pos;\n\n let currentTime = (_pos = _libraryInstance.getCurrentDate()) === null || _pos === void 0 ? null : _pos.getTime();\n\n if (currentTime && fragmentPos) {\n return {\n position: (fragmentPos + ping / 2 - currentTime) / 1000,\n act: fragmentPos + ping / 2,\n isBufferSufficient: true\n };\n } else {\n return {\n position: null,\n isBufferSufficient: false\n };\n }\n };\n\n const seekDoris = (time, act) => {\n //https://mcorp.no/lib/mediasync.js\n return new Promise((resolve, reject) => {\n if (time !== 0) {\n if (isSafari) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _libraryInstance.seekTo(new Date(act));\n } else {\n if (time > 0 && time < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n };\n\n let diff = time; // _videoElement.addEventListener('waiting', __, {once:true});\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime += time;\n }\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n let sgtid = null;\n\n const stalledGenerator = () => {\n clearTimeout(sgtid);\n sgtid = setTimeout(() => {\n if (_videoElement.paused === false) {\n handleStalledWaiting();\n }\n }, 1000);\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n dorisInstance,\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = dorisInstance || libraryInstance;\n _videoElement = _libraryInstance.sourceHandler.getElement();\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _videoElement.addEventListener('timeupdate', stalledGenerator);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_videoElement) {\n _videoElement.removeEventListener('timeupdate', stalledGenerator);\n\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDoris);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-doris.js?");
|
|
9251
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDoris = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n act,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n\n if (position && Math.abs(position) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekDoris(position, act).then(() => {\n const seekDuration = Date.now() - syncStartTime;\n const {\n position\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos + seekDuration, syncData.ping);\n const syncPrecision = Math.abs(position);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n var position;\n return (position = _libraryInstance.getCurrentDate()) === null || position === void 0 ? 0 : position.getTime();\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn, fragmentPos) {\n let ping = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n var _pos;\n\n let currentTime = (_pos = _libraryInstance.getCurrentDate()) === null || _pos === void 0 ? null : _pos.getTime();\n\n if (currentTime && fragmentPos) {\n return {\n position: (fragmentPos + ping / 2 - currentTime) / 1000,\n act: fragmentPos + ping / 2,\n isBufferSufficient: true\n };\n } else {\n return {\n position: null,\n isBufferSufficient: false\n };\n }\n };\n\n const seekDoris = (time, act) => {\n //https://mcorp.no/lib/mediasync.js\n return new Promise((resolve, reject) => {\n if (time !== 0) {\n if (isSafari) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _libraryInstance.seekTo(new Date(act));\n } else {\n if (time > 0 && time < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n };\n\n let diff = time; // _videoElement.addEventListener('waiting', __, {once:true});\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime += time;\n }\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n let sgtid = null;\n\n const stalledGenerator = () => {\n clearTimeout(sgtid);\n sgtid = setTimeout(() => {\n if (_videoElement.paused === false) {\n handleStalledWaiting();\n }\n }, 1000);\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n dorisInstance,\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = dorisInstance || libraryInstance;\n _videoElement = _libraryInstance.sourceHandler.getElement();\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _videoElement.addEventListener('timeupdate', stalledGenerator);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_videoElement) {\n _videoElement.removeEventListener('timeupdate', stalledGenerator);\n\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDoris);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-doris.js?");
|
|
9252
9252
|
|
|
9253
9253
|
/***/ }),
|
|
9254
9254
|
|
|
@@ -9260,7 +9260,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9260
9260
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9261
9261
|
|
|
9262
9262
|
"use strict";
|
|
9263
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodHlsJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const buffering = function (event) {\n let data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (data.details === 'bufferStalledError') {\n handleStalledWaiting();\n }\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n hlsInstance,\n libraryInstance,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = hlsInstance || libraryInstance; // backwards comp;\n\n _videoElement = _libraryInstance.media;\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_libraryInstance) {\n room._log('No hls.js player instance!');\n\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_libraryInstance && _videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _libraryInstance.on('hlsError', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('hlsError', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _libraryInstance = null;\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodHlsJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-hls-vod.js?");
|
|
9263
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodHlsJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const buffering = function (event) {\n let data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (data.details === 'bufferStalledError') {\n handleStalledWaiting();\n }\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n hlsInstance,\n libraryInstance,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = hlsInstance || libraryInstance; // backwards comp;\n\n _videoElement = _libraryInstance.media;\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_libraryInstance) {\n room._log('No hls.js player instance!');\n\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_libraryInstance && _videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _libraryInstance.on('hlsError', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('hlsError', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _libraryInstance = null;\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodHlsJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-hls-vod.js?");
|
|
9264
9264
|
|
|
9265
9265
|
/***/ }),
|
|
9266
9266
|
|
|
@@ -9272,7 +9272,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9272
9272
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9273
9273
|
|
|
9274
9274
|
"use strict";
|
|
9275
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n/*\n * TODO:\n * Sync we get\n * machineTimestamp = now\n * ping = (endRequestTimestamp - startRequestTimestamp) / 2\n * clockDifference = (serverTimestamp - machineTimestamp - ping)\n *\n *\n * usable vars : (now +- clockDifference) === currentServerTimestamp ---> when event was received\n * : (otherMachineTimestamp +- otherMachineClockDifference) === currentServerTimestamp ---> when event was sent\n *\n *\n * ping: (this user currentServerTimestamp a.k.a when event was received) - (remote user currentServerTimestamp a.k.a. when event was sent )\n *\n *\n * */\n\n\nconst syncHlsJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let currentFragmentSn = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n\n if (position && Math.abs(position) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekBy(position).then(() => {\n const seekDuration = Date.now() - syncStartTime;\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos + seekDuration, syncData.ping);\n const syncPrecision = Math.abs(position);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration / 1000));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const buffering = function (event) {\n let data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (data.details === 'bufferStalledError') {\n handleStalledWaiting();\n }\n };\n\n const hlsFragChanged = (event, data) => {\n currentFragmentSn = data.frag.sn;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n var _currentFragment$body;\n\n let fragments = _libraryInstance.streamController.fragmentTracker.fragments;\n\n if (!fragments) {\n return 0;\n }\n\n let currentFragment = fragments[Object.keys(fragments).find(key => key.indexOf(currentFragmentSn) > -1)];\n\n if (!currentFragment) {\n return 0;\n }\n\n return Math.max((_libraryInstance.streamController.lastCurrentTime - (((_currentFragment$body = currentFragment.body) === null || _currentFragment$body === void 0 ? void 0 : _currentFragment$body.startPTS) || 0)) * 1000, 0);\n };\n\n const calculateSyncDifferenceTime = (masterFragmentSn, masterFragmentPos, ping) => {\n var _currentFragment$body2;\n\n let fragments = _libraryInstance.streamController.fragmentTracker.fragments;\n let fragmentNumbers = Object.keys(fragments).map(key => parseInt(key.slice(key.lastIndexOf('_') + 1))).sort(function (a, b) {\n return a - b;\n }); // our position in fragment\n //let currentFragmentSn = currentFragmentSn;\n\n let currentFragment = fragments[Object.keys(fragments).find(key => key.indexOf(currentFragmentSn) > -1)];\n\n if (!currentFragment) {\n room._log('Current fragment not found, what now?', currentFragmentSn);\n\n return {\n position: null,\n isBufferSufficient: false\n };\n }\n\n let currentFragmentPos = Math.max((_libraryInstance.streamController.lastCurrentTime - (((_currentFragment$body2 = currentFragment.body) === null || _currentFragment$body2 === void 0 ? void 0 : _currentFragment$body2.startPTS) || 0)) * 1000, 0);\n let currentFragmentLength = currentFragment.body.duration * 1000;\n\n room._log(\"Our current fragment is: \".concat(currentFragmentSn));\n\n room._log(\"Our current fragment position is: \".concat(currentFragmentPos));\n\n room._log(\"Our current fragment length is: \".concat(currentFragmentLength)); // real server fragment\n\n\n let realMasterFragmentSn = masterFragmentSn; // real server position in fragment\n\n let realMasterFragmentPos = Math.max(ping / 2 + masterFragmentPos, 0); // simple flag\n\n let isBufferSufficient = false;\n /* Searching for fragment and in-fragment position when taking all delays into account */\n\n let relevantFragmentNumbers = fragmentNumbers.indexOf(masterFragmentSn) > -1 ? fragmentNumbers.slice(fragmentNumbers.indexOf(masterFragmentSn)) : [];\n\n for (let i = 0, len = relevantFragmentNumbers.length; i < len; i++) {\n let sn = relevantFragmentNumbers[i];\n let fragment = fragments[Object.keys(fragments).find(key => key.indexOf(sn) > -1)];\n let fragmentDuration = fragment.body.duration * 1000;\n\n room._log(\"Correction diff: \".concat(realMasterFragmentPos - fragmentDuration));\n\n realMasterFragmentSn = sn;\n\n if (realMasterFragmentPos - fragmentDuration > 0) {\n room._log(\"Not enough at fragment \".concat(sn));\n\n realMasterFragmentPos -= fragmentDuration;\n } else {\n room._log(\"Enough at fragment \".concat(sn));\n\n isBufferSufficient = true;\n break;\n }\n } // We're not ready yet\n\n\n if (!isBufferSufficient) {\n room._log(\"We don't have required fragment \".concat(realMasterFragmentSn, \" yet\"));\n\n return {\n position: null,\n isBufferSufficient: false\n };\n } // We are too ahead\n\n\n if (realMasterFragmentSn < currentFragmentSn) {\n room._log(\"We are ahead of master...\");\n\n room._log(\"Real master fragment: \".concat(realMasterFragmentSn));\n\n room._log(\"Our fragment: \".concat(currentFragmentSn));\n\n let seekTime = currentFragmentPos - realMasterFragmentPos;\n let relevantFragmentNumbers = fragmentNumbers.slice(fragmentNumbers.indexOf(realMasterFragmentSn));\n\n for (let i = 0, len = relevantFragmentNumbers.length; i < len; i++) {\n let sn = relevantFragmentNumbers[i];\n let fragment = fragments[Object.keys(fragments).find(key => key.indexOf(sn) > -1)];\n let fragmentDuration = fragment.body.duration * 1000;\n\n if (sn >= currentFragmentSn) {\n break;\n }\n\n seekTime += fragmentDuration;\n }\n\n room._log(\"Sync difference is: \".concat(seekTime));\n\n return {\n position: seekTime / 1000 * -1,\n isBufferSufficient: true\n };\n } // We are behind or spot on\n else {\n room._log(\"We're behind or spot on master...\");\n\n room._log(\"Real master fragment: \".concat(realMasterFragmentSn));\n\n room._log(\"Our fragment: \".concat(currentFragmentSn));\n\n let seekTime = realMasterFragmentPos - currentFragmentPos;\n let relevantFragmentNumbers = fragmentNumbers.slice(fragmentNumbers.indexOf(currentFragmentSn));\n\n for (let i = 0, len = relevantFragmentNumbers.length; i < len; i++) {\n let sn = relevantFragmentNumbers[i];\n let fragment = fragments[Object.keys(fragments).find(key => key.indexOf(sn) > -1)];\n let fragmentDuration = fragment.body.duration * 1000;\n\n if (sn >= realMasterFragmentSn) {\n break;\n }\n\n seekTime += fragmentDuration;\n }\n\n room._log(\"We can proceed with seek time: \".concat(seekTime));\n\n return {\n position: seekTime / 1000,\n isBufferSufficient: true\n };\n }\n };\n\n const seekBy = time => {\n //https://mcorp.no/lib/mediasync.js\n return new Promise((resolve, reject) => {\n if (time !== 0) {\n if (time > 0 && time < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n };\n\n let diff = time; // _videoElement.addEventListener('waiting', __, {once:true});\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime += time;\n }\n } else resolve();\n });\n };\n\n const seekTo = time => {\n //https://webtiming.github.io/timingsrc/ use this\n //https://mcorp.no/lib/mediasync.js\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragment = currentFragmentSn;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Fragment: \".concat(fragment));\n\n room._log(\"Fragment position: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(fragment),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragment = currentFragmentSn;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(fragment),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n hlsInstance,\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = hlsInstance || libraryInstance; // backwards comp;\n\n _videoElement = _libraryInstance.media;\n\n if (!_libraryInstance) {\n room._log('No hls.js player instance!');\n\n return;\n }\n\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _libraryInstance.once('hlsFragChanged', () => {\n restartSyncLoop();\n });\n }\n\n _libraryInstance.on('hlsError', buffering);\n\n _libraryInstance.on('hlsFragChanged', hlsFragChanged);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('hlsError', buffering);\n\n _libraryInstance.off('hlsFragChanged', hlsFragChanged);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncHlsJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-hls.js?");
|
|
9275
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n/*\n * TODO:\n * Sync we get\n * machineTimestamp = now\n * ping = (endRequestTimestamp - startRequestTimestamp) / 2\n * clockDifference = (serverTimestamp - machineTimestamp - ping)\n *\n *\n * usable vars : (now +- clockDifference) === currentServerTimestamp ---> when event was received\n * : (otherMachineTimestamp +- otherMachineClockDifference) === currentServerTimestamp ---> when event was sent\n *\n *\n * ping: (this user currentServerTimestamp a.k.a when event was received) - (remote user currentServerTimestamp a.k.a. when event was sent )\n *\n *\n * */\n\n\nconst syncHlsJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let currentFragmentSn = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n\n if (position && Math.abs(position) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekBy(position).then(() => {\n const seekDuration = Date.now() - syncStartTime;\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos + seekDuration, syncData.ping);\n const syncPrecision = Math.abs(position);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration / 1000));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const buffering = function (event) {\n let data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (data.details === 'bufferStalledError') {\n handleStalledWaiting();\n }\n };\n\n const hlsFragChanged = (event, data) => {\n currentFragmentSn = data.frag.sn;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n var _currentFragment$body;\n\n let fragments = _libraryInstance.streamController.fragmentTracker.fragments;\n\n if (!fragments) {\n return 0;\n }\n\n let currentFragment = fragments[Object.keys(fragments).find(key => key.indexOf(currentFragmentSn) > -1)];\n\n if (!currentFragment) {\n return 0;\n }\n\n return Math.max((_libraryInstance.streamController.lastCurrentTime - (((_currentFragment$body = currentFragment.body) === null || _currentFragment$body === void 0 ? void 0 : _currentFragment$body.startPTS) || 0)) * 1000, 0);\n };\n\n const calculateSyncDifferenceTime = (masterFragmentSn, masterFragmentPos, ping) => {\n var _currentFragment$body2;\n\n let fragments = _libraryInstance.streamController.fragmentTracker.fragments;\n let fragmentNumbers = Object.keys(fragments).map(key => parseInt(key.slice(key.lastIndexOf('_') + 1))).sort(function (a, b) {\n return a - b;\n }); // our position in fragment\n //let currentFragmentSn = currentFragmentSn;\n\n let currentFragment = fragments[Object.keys(fragments).find(key => key.indexOf(currentFragmentSn) > -1)];\n\n if (!currentFragment) {\n room._log('Current fragment not found, what now?', currentFragmentSn);\n\n return {\n position: null,\n isBufferSufficient: false\n };\n }\n\n let currentFragmentPos = Math.max((_libraryInstance.streamController.lastCurrentTime - (((_currentFragment$body2 = currentFragment.body) === null || _currentFragment$body2 === void 0 ? void 0 : _currentFragment$body2.startPTS) || 0)) * 1000, 0);\n let currentFragmentLength = currentFragment.body.duration * 1000;\n\n room._log(\"Our current fragment is: \".concat(currentFragmentSn));\n\n room._log(\"Our current fragment position is: \".concat(currentFragmentPos));\n\n room._log(\"Our current fragment length is: \".concat(currentFragmentLength)); // real server fragment\n\n\n let realMasterFragmentSn = masterFragmentSn; // real server position in fragment\n\n let realMasterFragmentPos = Math.max(ping / 2 + masterFragmentPos, 0); // simple flag\n\n let isBufferSufficient = false;\n /* Searching for fragment and in-fragment position when taking all delays into account */\n\n let relevantFragmentNumbers = fragmentNumbers.indexOf(masterFragmentSn) > -1 ? fragmentNumbers.slice(fragmentNumbers.indexOf(masterFragmentSn)) : [];\n\n for (let i = 0, len = relevantFragmentNumbers.length; i < len; i++) {\n let sn = relevantFragmentNumbers[i];\n let fragment = fragments[Object.keys(fragments).find(key => key.indexOf(sn) > -1)];\n let fragmentDuration = fragment.body.duration * 1000;\n\n room._log(\"Correction diff: \".concat(realMasterFragmentPos - fragmentDuration));\n\n realMasterFragmentSn = sn;\n\n if (realMasterFragmentPos - fragmentDuration > 0) {\n room._log(\"Not enough at fragment \".concat(sn));\n\n realMasterFragmentPos -= fragmentDuration;\n } else {\n room._log(\"Enough at fragment \".concat(sn));\n\n isBufferSufficient = true;\n break;\n }\n } // We're not ready yet\n\n\n if (!isBufferSufficient) {\n room._log(\"We don't have required fragment \".concat(realMasterFragmentSn, \" yet\"));\n\n return {\n position: null,\n isBufferSufficient: false\n };\n } // We are too ahead\n\n\n if (realMasterFragmentSn < currentFragmentSn) {\n room._log(\"We are ahead of master...\");\n\n room._log(\"Real master fragment: \".concat(realMasterFragmentSn));\n\n room._log(\"Our fragment: \".concat(currentFragmentSn));\n\n let seekTime = currentFragmentPos - realMasterFragmentPos;\n let relevantFragmentNumbers = fragmentNumbers.slice(fragmentNumbers.indexOf(realMasterFragmentSn));\n\n for (let i = 0, len = relevantFragmentNumbers.length; i < len; i++) {\n let sn = relevantFragmentNumbers[i];\n let fragment = fragments[Object.keys(fragments).find(key => key.indexOf(sn) > -1)];\n let fragmentDuration = fragment.body.duration * 1000;\n\n if (sn >= currentFragmentSn) {\n break;\n }\n\n seekTime += fragmentDuration;\n }\n\n room._log(\"Sync difference is: \".concat(seekTime));\n\n return {\n position: seekTime / 1000 * -1,\n isBufferSufficient: true\n };\n } // We are behind or spot on\n else {\n room._log(\"We're behind or spot on master...\");\n\n room._log(\"Real master fragment: \".concat(realMasterFragmentSn));\n\n room._log(\"Our fragment: \".concat(currentFragmentSn));\n\n let seekTime = realMasterFragmentPos - currentFragmentPos;\n let relevantFragmentNumbers = fragmentNumbers.slice(fragmentNumbers.indexOf(currentFragmentSn));\n\n for (let i = 0, len = relevantFragmentNumbers.length; i < len; i++) {\n let sn = relevantFragmentNumbers[i];\n let fragment = fragments[Object.keys(fragments).find(key => key.indexOf(sn) > -1)];\n let fragmentDuration = fragment.body.duration * 1000;\n\n if (sn >= realMasterFragmentSn) {\n break;\n }\n\n seekTime += fragmentDuration;\n }\n\n room._log(\"We can proceed with seek time: \".concat(seekTime));\n\n return {\n position: seekTime / 1000,\n isBufferSufficient: true\n };\n }\n };\n\n const seekBy = time => {\n //https://mcorp.no/lib/mediasync.js\n return new Promise((resolve, reject) => {\n if (time !== 0) {\n if (time > 0 && time < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n };\n\n let diff = time; // _videoElement.addEventListener('waiting', __, {once:true});\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime += time;\n }\n } else resolve();\n });\n };\n\n const seekTo = time => {\n //https://webtiming.github.io/timingsrc/ use this\n //https://mcorp.no/lib/mediasync.js\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragment = currentFragmentSn;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Fragment: \".concat(fragment));\n\n room._log(\"Fragment position: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(fragment),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragment = currentFragmentSn;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(fragment),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n hlsInstance,\n libraryInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = hlsInstance || libraryInstance; // backwards comp;\n\n _videoElement = _libraryInstance.media;\n\n if (!_libraryInstance) {\n room._log('No hls.js player instance!');\n\n return;\n }\n\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _libraryInstance.once('hlsFragChanged', () => {\n restartSyncLoop();\n });\n }\n\n _libraryInstance.on('hlsError', buffering);\n\n _libraryInstance.on('hlsFragChanged', hlsFragChanged);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.off('hlsError', buffering);\n\n _libraryInstance.off('hlsFragChanged', hlsFragChanged);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncHlsJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-hls.js?");
|
|
9276
9276
|
|
|
9277
9277
|
/***/ }),
|
|
9278
9278
|
|
|
@@ -9284,7 +9284,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9284
9284
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9285
9285
|
|
|
9286
9286
|
"use strict";
|
|
9287
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodHlsJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n restartSyncLoop();\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n videoElement,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _videoElement = videoElement;\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_videoElement) {\n room._log('No video element present!');\n\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _videoElement.addEventListener('stalled', handleStalledWaiting);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_videoElement) {\n _videoElement.removeEventListener('stalled', handleStalledWaiting);\n\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodHlsJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-native-hls-vod.js?");
|
|
9287
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodHlsJs = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n restartSyncLoop();\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n videoElement,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _videoElement = videoElement;\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_videoElement) {\n room._log('No video element present!');\n\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _videoElement.addEventListener('stalled', handleStalledWaiting);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_videoElement) {\n _videoElement.removeEventListener('stalled', handleStalledWaiting);\n\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodHlsJs);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-native-hls-vod.js?");
|
|
9288
9288
|
|
|
9289
9289
|
/***/ }),
|
|
9290
9290
|
|
|
@@ -9296,7 +9296,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9296
9296
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9297
9297
|
|
|
9298
9298
|
"use strict";
|
|
9299
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncNativeHls = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPosition: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n let sgtid = null;\n\n const stalledGenerator = () => {\n clearTimeout(sgtid);\n sgtid = setTimeout(() => {\n if (_videoElement.paused === false) {\n handleStalledWaiting();\n }\n }, 1000);\n };\n\n const roomSyncSend = slaveId => {\n if (!_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n videoElement,\n syncOnLevelSwitch = false,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _videoElement = videoElement;\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _videoElement.addEventListener('timeupdate', stalledGenerator);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_videoElement) {\n _videoElement.removeEventListener('timeupdate', stalledGenerator);\n\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncNativeHls);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-native-hls.js?");
|
|
9299
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncNativeHls = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPosition: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n let sgtid = null;\n\n const stalledGenerator = () => {\n clearTimeout(sgtid);\n sgtid = setTimeout(() => {\n if (_videoElement.paused === false) {\n handleStalledWaiting();\n }\n }, 1000);\n };\n\n const roomSyncSend = slaveId => {\n if (!_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n videoElement,\n syncOnLevelSwitch = false,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _videoElement = videoElement;\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _videoElement.addEventListener('timeupdate', stalledGenerator);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_videoElement) {\n _videoElement.removeEventListener('timeupdate', stalledGenerator);\n\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncNativeHls);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-native-hls.js?");
|
|
9300
9300
|
|
|
9301
9301
|
/***/ }),
|
|
9302
9302
|
|
|
@@ -9308,7 +9308,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9308
9308
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9309
9309
|
|
|
9310
9310
|
"use strict";
|
|
9311
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodShakaDash = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n restartSyncLoop();\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const buffering = event => {\n if (event.buffering) {\n handleStalledWaiting();\n }\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n shakaInstance,\n libraryInstance,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = shakaInstance || libraryInstance;\n _videoElement = _libraryInstance.getMediaElement();\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_libraryInstance) {\n console.log('No shaka player instance');\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_libraryInstance && _videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _libraryInstance.addEventListener('buffering', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_libraryInstance) {\n _libraryInstance.removeEventListener('buffering', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _libraryInstance = null;\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodShakaDash);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-shaka-dash-vod.js?");
|
|
9311
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncVodShakaDash = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n let _roomId = null;\n let _syncDisabled = false;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let seekingDebounceId = null;\n let seekingDebounceTimeout = 300;\n let isPlaying = false;\n let isProgrammaticallySeeked = false;\n let isForcedMaster = false;\n\n const startSyncLoop = () => {\n if (_syncDisabled) {\n room._log('--- Sync loop will not start due to sync force disabled ---');\n\n return;\n }\n\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster || stopFlag) {\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (isForcedMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (syncData.masterFragmentSn) {\n playMedia();\n } else {\n pauseMedia();\n }\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(position + (isPlaying ? seekDuration : 0) - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement) {\n //TODO: shit fix ... data channel with other participant is not opened yet so we wait\n setTimeout(() => {\n restartSyncLoop();\n }, 1000);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const handleRemoveRemoteParticipant = () => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const buffering = event => {\n if (event.buffering) {\n handleStalledWaiting();\n }\n };\n\n const executePlayerIotEvents = data => {\n let rid = _roomId || room.roomId;\n\n if (data.attributeName === 'playerReplay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n seekTo(0).then(() => playMedia());\n }\n\n if (data.attributeName === 'playerPlay' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n playMedia();\n }\n\n if (data.attributeName === 'playerPause' && (!rid || (data === null || data === void 0 ? void 0 : data.roomId) === rid)) {\n pauseMedia();\n }\n };\n\n const parseIotEvents = data => {\n if (data.event === 'template_updated') {\n if (data.operation === 'multi') {\n (data.value || []).forEach(event => {\n if (event.operation === 'sendMessage') {\n executePlayerIotEvents({ ...event,\n roomId: data.roomId,\n userId: data.userId\n });\n }\n });\n }\n\n if (data.operation === 'sendMessage') {\n executePlayerIotEvents(data);\n }\n }\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n } else if (msg.videoroom === 'message') {\n // ignoring non studio commands if master\n if (isForcedMaster && msg.from !== null) {\n return;\n }\n\n if (_syncDisabled) {\n return;\n }\n\n if (msg.user_action === 'sync_seeking') {\n seekMedia(msg).catch(() => {});\n } else if (msg.user_action === 'sync_seeked') {} else if (msg.user_action === 'sync_play' || msg.action === 'sync_play') {\n playMedia();\n } else if (msg.user_action === 'sync_pause' || msg.action === 'sync_pause') {\n pauseMedia();\n }\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = function (fragmentSn) {\n let fragmentPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n let ping = arguments.length > 2 ? arguments[2] : undefined;\n return {\n position: (fragmentPos + ping / 2) / 1000,\n isBufferSufficient: true\n };\n };\n\n const seekMedia = msg => {\n msg = {\n userId: msg.from,\n ...(msg.text && JSON.parse(msg.text))\n };\n\n if (msg.handleId !== room.handleId) {\n const {\n position,\n isBufferSufficient\n } = calculateSyncDifferenceTime(msg.fragment, msg.fragment_pos, 0); //TODO: right ping\n\n if (position) {\n return seekTo(position).then(() => {\n if (isForcedMaster) {\n propagateMasterFunc();\n }\n });\n } else {\n return Promise.resolve();\n }\n } else {\n return Promise.resolve();\n }\n };\n\n const prePlay = () => {\n if (_videoElement.currentTime > 0 || _videoElement.paused === false) {\n return Promise.resolve();\n }\n\n isProgrammaticallySeeked = true;\n let wasMuted = _videoElement.muted;\n _videoElement.muted = true;\n return _videoElement.play().then(() => {\n _videoElement.pause();\n\n _videoElement.currentTime = 0;\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"wait\"])(100);\n }).catch(() => true).finally(() => {\n _videoElement.muted = wasMuted;\n isProgrammaticallySeeked = false;\n return true;\n });\n };\n\n const playMedia = () => {\n if (_videoElement && _videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.play().finally(() => {\n isProgrammaticallySeeked = false;\n });\n }\n };\n\n const pauseMedia = () => {\n if (_videoElement && !_videoElement.paused) {\n isProgrammaticallySeeked = true;\n\n _videoElement.addEventListener('pause', () => {\n isProgrammaticallySeeked = false;\n }, {\n once: true\n });\n\n _videoElement.pause();\n }\n };\n\n const seekTo = time => {\n return new Promise(resolve => {\n if (_videoElement.currentTime !== time) {\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('seeked', () => {\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_play', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n if (!isProgrammaticallySeeked) {\n roomSession.sendSystemMessage('sync_pause', {\n timestamp: new Date().getTime(),\n handleId: room.handleId\n }, undefined, null).catch(() => {});\n }\n\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n if (!isProgrammaticallySeeked) {\n clientPaused().catch(() => {});\n }\n };\n\n const handleSeeking = () => {\n clearTimeout(seekingDebounceId);\n\n if (!isProgrammaticallySeeked) {\n seekingDebounceId = setTimeout(() => {\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n roomSession.sendSystemMessage('sync_seeking', {\n timestamp: new Date().getTime(),\n handleId: room.handleId,\n fragment: \"0\",\n //isPlaying ? \"1\":\"0\",\n fragment_pos: Number(fragmentPosition)\n }, undefined, null).catch(() => {});\n\n if (!isForcedMaster && !_syncDisabled) {\n pauseMedia();\n }\n }, seekingDebounceTimeout);\n }\n };\n\n const handleSeeked = () => {};\n\n const handleEnded = () => {};\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: isPlaying ? \"1\" : \"0\",\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n shakaInstance,\n libraryInstance,\n propagateMaster = false,\n isStudio = false,\n roomId,\n syncDisabled\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = shakaInstance || libraryInstance;\n _videoElement = _libraryInstance.getMediaElement();\n _roomId = roomId;\n _syncDisabled = syncDisabled;\n\n if (!_libraryInstance) {\n console.log('No shaka player instance');\n return;\n }\n\n emitter.emit('playerSyncing', false);\n isForcedMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.on('data', parseDataEvents);\n wt.iot.$on('message', parseIotEvents);\n\n if (isForcedMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_libraryInstance && _videoElement) {\n (propagateMaster || isStudio ? Promise.resolve() : prePlay()).then(() => {\n _libraryInstance.addEventListener('buffering', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n\n _videoElement.addEventListener('seeking', handleSeeking);\n\n _videoElement.addEventListener('seeked', handleSeeked);\n\n _videoElement.addEventListener('ended', handleEnded);\n\n startSyncLoop();\n });\n }\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('removeRemoteParticipant', handleRemoveRemoteParticipant);\n room.off('data', parseDataEvents);\n wt.iot.$off('message', parseIotEvents);\n\n if (_libraryInstance) {\n _libraryInstance.removeEventListener('buffering', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n\n _videoElement.removeEventListener('seeking', handleSeeking);\n\n _videoElement.removeEventListener('seeked', handleSeeked);\n\n _videoElement.removeEventListener('ended', handleEnded);\n }\n\n _libraryInstance = null;\n _videoElement = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncVodShakaDash);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-shaka-dash-vod.js?");
|
|
9312
9312
|
|
|
9313
9313
|
/***/ }),
|
|
9314
9314
|
|
|
@@ -9320,7 +9320,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9320
9320
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9321
9321
|
|
|
9322
9322
|
"use strict";
|
|
9323
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDaznDash = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const buffering = event => {\n if (event.buffering) {\n handleStalledWaiting();\n }\n };\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPosition: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n }; // const seekTo = (time) => {\n // return new Promise((resolve, reject) => {\n // if(_videoElement.currentTime !== time) {\n // let __ = Date.now();\n // isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n // _videoElement.addEventListener('playing', () => {\n // room._log(`It took the player ${(Date.now() - __) / 1000} seconds to seek `);\n // isProgrammaticallySeeked = false;\n // resolve();\n // }, {once:true});\n // _videoElement.currentTime = time;\n // } else resolve()\n // });\n // };\n\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n libraryInstance,\n shakaInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = libraryInstance || shakaInstance;\n _videoElement = _libraryInstance.getMediaElement();\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _libraryInstance.addEventListener('buffering', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.removeEventListener('buffering', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDaznDash);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-shaka-dash.js?");
|
|
9323
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n\n\nconst syncDaznDash = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n //SYNC VARS\n let _libraryInstance = null;\n let _videoElement = null;\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const buffering = event => {\n if (event.buffering) {\n handleStalledWaiting();\n }\n };\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_videoElement.paused) _videoElement.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _videoElement && _videoElement.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _videoElement.buffered;\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n realPosition: position,\n isBufferSufficient: false\n };\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_videoElement.currentTime !== time) {\n let diff = time - _videoElement.currentTime;\n\n if (_videoElement.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let __ = () => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false;\n reject('Stalled');\n }; // _videoElement.addEventListener('waiting', __, {once:true});\n\n\n _videoElement.addEventListener('stalled', __, {\n once: true\n });\n\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n if (wasPaused) {\n _videoElement.pause();\n }\n\n _videoElement.playbackRate = 1;\n isProgrammaticallySeeked = false; // _videoElement.removeEventListener('waiting', __, {once:true});\n\n _videoElement.removeEventListener('stalled', __, {\n once: true\n });\n\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n isProgrammaticallySeeked = true;\n\n if (_videoElement.paused) {\n wasPaused = true;\n\n _videoElement.play().then(() => {\n _videoElement.playbackRate = playbackRate;\n });\n } else {\n _videoElement.playbackRate = playbackRate;\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let __ = Date.now();\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _videoElement.addEventListener('playing', () => {\n room._log(\"It took the player \".concat((Date.now() - __) / 1000, \" seconds to seek \"));\n\n isProgrammaticallySeeked = false;\n resolve();\n }, {\n once: true\n });\n\n _videoElement.currentTime = time;\n }\n } else resolve();\n });\n }; // const seekTo = (time) => {\n // return new Promise((resolve, reject) => {\n // if(_videoElement.currentTime !== time) {\n // let __ = Date.now();\n // isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n // _videoElement.addEventListener('playing', () => {\n // room._log(`It took the player ${(Date.now() - __) / 1000} seconds to seek `);\n // isProgrammaticallySeeked = false;\n // resolve();\n // }, {once:true});\n // _videoElement.currentTime = time;\n // } else resolve()\n // });\n // };\n\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _videoElement.paused;\n isPlaying = !_videoElement.paused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_videoElement.paused;\n };\n\n const handleStalledWaiting = () => {\n room._log('handleStalledWaiting');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _videoElement.paused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_libraryInstance || !_videoElement) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n initialize: function () {\n let {\n libraryInstance,\n shakaInstance,\n propagateMaster = false\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _libraryInstance = libraryInstance || shakaInstance;\n _videoElement = _libraryInstance.getMediaElement();\n shouldPropagateMaster = propagateMaster;\n isPlaying = _videoElement.paused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_videoElement && _videoElement.paused === false) {\n _videoElement.addEventListener('progress', () => {\n restartSyncLoop();\n }, {\n once: true\n });\n }\n\n _libraryInstance.addEventListener('buffering', buffering);\n\n _videoElement.addEventListener('playing', handlePlaying);\n\n _videoElement.addEventListener('pause', handlePause);\n },\n destroy: () => {\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (_libraryInstance) {\n _libraryInstance.removeEventListener('buffering', buffering);\n }\n\n if (_videoElement) {\n _videoElement.removeEventListener('playing', handlePlaying);\n\n _videoElement.removeEventListener('pause', handlePause);\n }\n\n _libraryInstance = null;\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncDaznDash);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-shaka-dash.js?");
|
|
9324
9324
|
|
|
9325
9325
|
/***/ }),
|
|
9326
9326
|
|
|
@@ -9332,7 +9332,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_
|
|
|
9332
9332
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9333
9333
|
|
|
9334
9334
|
"use strict";
|
|
9335
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../wt-emitter */ \"./src/modules/wt-emitter.js\");\n\n\n\nconst syncUniversal = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n //SYNC VARS\n let _emitter = Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n\n let _playerInterface = null;\n let _playerInterfaceOptions = {\n isLive: true,\n ignoreBufferedTimeRanges: true,\n useSeekBy: false,\n syncBySegments: false,\n disableFastSeek: false\n };\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n const waitForPlayingEventAfterSeek = 5000;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_playerInterface.isPaused) _playerInterface.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(realPosition, seekDuration, getCurrentSegmentPosition());\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration, \" seconds\"));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_playerInterface.isPaused === false) {\n _emitter.once('timeupdate', restartSyncLoop);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room._hasJoined;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _playerInterface.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _playerInterface.buffered;\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (seekRanges && !_playerInterfaceOptions.ignoreBufferedTimeRanges) {\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n realPosition: position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n isBufferSufficient: false\n };\n } else {\n return {\n position,\n realPosition: position,\n isBufferSufficient: true\n };\n }\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_playerInterface.currentTime !== time) {\n let diff = time - _playerInterface.currentTime;\n\n if (_playerInterface.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let _handleFailed = () => {\n if (wasPaused) {\n _playerInterface.pause();\n }\n\n _playerInterface.setPlaybackRate(1);\n\n isProgrammaticallySeeked = false;\n reject('Stalled');\n };\n\n let _fastForward = () => {\n isProgrammaticallySeeked = true;\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n _emitter.off('stalled', _handleFailed);\n\n if (wasPaused && !_playerInterfaceOptions.isLive) {\n _playerInterface.pause();\n }\n\n _playerInterface.setPlaybackRate(1);\n\n isProgrammaticallySeeked = false;\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n\n _playerInterface.setPlaybackRate(playbackRate);\n };\n\n _emitter.once('stalled', _handleFailed);\n\n if (_playerInterface.isPaused) {\n wasPaused = true;\n\n _playerInterface.play().then(() => {\n _fastForward();\n }).catch(_handleFailed);\n } else {\n _fastForward();\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let currentTimestamp = Date.now();\n let __failsafeId = null;\n\n let _resolve = () => {\n clearTimeout(__failsafeId);\n __failsafeId = null;\n isProgrammaticallySeeked = false;\n\n room._log(\"It took the player \".concat((Date.now() - currentTimestamp) / 1000, \" seconds to seek \"));\n\n resolve();\n };\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _emitter.once('playing', _resolve);\n\n __failsafeId = setTimeout(_resolve, waitForPlayingEventAfterSeek);\n\n _playerInterface.seek(time);\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _playerInterface.isPaused;\n isPlaying = !_playerInterface.isPaused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_playerInterface.isPaused;\n };\n\n const handleBuffering = () => {\n room._log('handleBuffering');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _playerInterface.isPaused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_playerInterface) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n getHandlers: () => ({\n handlePause: event => {\n room._log('handlePause');\n\n _emitter.emit('pause', event);\n },\n handlePlaying: event => {\n room._log('handlePlaying');\n\n _emitter.emit('playing', event);\n },\n handleBuffering: event => {\n room._log('handleBuffering');\n\n _emitter.emit('buffering', event);\n },\n handleStalled: event => {\n room._log('handleBuffering');\n\n room._log('handleStalled');\n\n _emitter.emit('buffering', event);\n\n _emitter.emit('stalled', event);\n },\n handleTimeupdate: event => {\n room._log('handleTimeupdate');\n\n _emitter.emit('timeupdate', event);\n }\n }),\n initialize: function () {\n let {\n playerInterface\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _playerInterface = playerInterface;\n _playerInterfaceOptions = { ..._playerInterfaceOptions,\n ...(_playerInterface.options || {})\n };\n\n room._log('Interface options passed: ', _playerInterface.options || {});\n\n room._log('All interface options: ', _playerInterfaceOptions);\n\n shouldPropagateMaster = false;\n isPlaying = _playerInterface.isPaused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_playerInterface.isPaused === false) {\n _emitter.once('timeupdate', restartSyncLoop);\n }\n\n _emitter.on('buffering', handleBuffering);\n\n _emitter.on('playing', handlePlaying);\n\n _emitter.on('pause', handlePause);\n },\n destroy: () => {\n var _playerInterface2;\n\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (typeof ((_playerInterface2 = _playerInterface) === null || _playerInterface2 === void 0 ? void 0 : _playerInterface2.destroy) === 'function') {\n var _playerInterface3;\n\n (_playerInterface3 = _playerInterface) === null || _playerInterface3 === void 0 ? void 0 : _playerInterface3.destroy();\n }\n\n _playerInterface = null;\n\n _emitter.clear();\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncUniversal);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-universal.js?");
|
|
9335
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../wt-utils */ \"./src/modules/wt-utils.js\");\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../wt-emitter */ \"./src/modules/wt-emitter.js\");\n\n\n\nconst syncUniversal = function () {\n let {\n room,\n wt,\n roomSession,\n emitter\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n //SYNC VARS\n let _emitter = Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n\n let _playerInterface = null;\n let _playerInterfaceOptions = {\n isLive: true,\n ignoreBufferedTimeRanges: true,\n useSeekBy: false,\n syncBySegments: false,\n disableFastSeek: false\n };\n const syncDefaultWaitTime = 60000;\n const syncShortWaitTime = 10000;\n const maxSyncThreshold = 0.5;\n const maxSyncTries = 3;\n const waitForPlayingEventAfterSeek = 5000;\n let currentSyncRetry = 0;\n let syncWaitId = null;\n let syncNextWaitTime = null;\n let stopFlag = false;\n let isSyncing = false;\n let playbackRate = 2;\n let isPlaying = false;\n let isPreloading = true;\n let isProgrammaticallySeeked = false;\n let shouldPropagateMaster = false;\n\n const startSyncLoop = () => {\n if (!isConnected()) {\n room._log('--- Sync loop will not start due to user not connected yet ---');\n\n return;\n }\n\n if (syncWaitId) {\n room._log('--- Sync loop already running ---');\n\n return;\n }\n\n room._log('--- Sync enabled ---');\n\n stopFlag = false;\n\n const loop = () => {\n isSyncing = true;\n emitter.emit('playerSyncing', true);\n sync().finally(() => {\n isSyncing = false;\n emitter.emit('playerSyncing', false);\n\n if (isConnected() && !stopFlag) {\n syncWaitId = setTimeout(loop, syncNextWaitTime);\n } else {\n room._log('--- Automatic stop due to user not connected or stop flag enabled ---');\n\n stopSyncLoop();\n }\n });\n };\n\n loop();\n };\n\n const stopSyncLoop = () => {\n room._log('--- Sync disabled ---');\n\n clearTimeout(syncWaitId);\n syncWaitId = null;\n currentSyncRetry = 0;\n stopFlag = true;\n };\n\n const restartSyncLoop = () => {\n room._log('--- Sync restarting ---');\n\n stopSyncLoop();\n startSyncLoop();\n };\n\n const setNextWaitTime = function () {\n let didSyncFail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (!didSyncFail) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n currentSyncRetry++;\n\n if (currentSyncRetry > maxSyncTries) {\n syncNextWaitTime = syncDefaultWaitTime;\n currentSyncRetry = 0;\n } else {\n syncNextWaitTime = syncShortWaitTime;\n }\n }\n\n room._log('--- Next sync will occur in ' + syncNextWaitTime / 1000 + ' seconds ---');\n };\n\n const sync = () => {\n return getSyncData().then(syncData => {\n if (syncData.isMaster) {\n if (_playerInterface.isPaused) _playerInterface.play();\n setNextWaitTime(false);\n return Promise.resolve();\n } else if (shouldPropagateMaster) {\n setNextWaitTime(false);\n return propagateMasterFunc();\n } else {\n const syncStartTime = Date.now();\n const {\n position,\n realPosition,\n isBufferSufficient\n } = calculateSyncDifferenceTime(syncData.masterFragmentSn, syncData.masterFragmentPos, syncData.ping);\n const currentPosition = getCurrentSegmentPosition() / 1000;\n\n if (position && Math.abs(position - currentPosition) <= maxSyncThreshold) {\n room._log(\"We're within max sync threshold, no need to resync now\");\n\n setNextWaitTime(false);\n return Promise.resolve();\n }\n\n if (position !== null) {\n return seekTo(position).then(() => {\n const seekDuration = (Date.now() - syncStartTime) / 1000;\n const syncPrecision = Math.abs(realPosition + seekDuration - getCurrentSegmentPosition() / 1000);\n\n room._log(realPosition, seekDuration, getCurrentSegmentPosition());\n\n room._log(\"Insufficient buffer: \", !isBufferSufficient);\n\n room._log(\"Seek duration is \".concat(seekDuration, \" seconds\"));\n\n room._log(\"Sync precision should be \".concat(syncPrecision));\n\n const didSyncFail = syncPrecision > maxSyncThreshold;\n setNextWaitTime(didSyncFail);\n return Promise.resolve();\n }).catch(e => {\n setNextWaitTime(true);\n return Promise.reject(e);\n });\n } else {\n setNextWaitTime(true);\n return Promise.reject();\n }\n }\n }).catch(() => {\n setNextWaitTime(true);\n return Promise.reject();\n });\n };\n\n const handleAddLocalParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n\n if (_playerInterface.isPaused === false) {\n _emitter.once('timeupdate', restartSyncLoop);\n }\n };\n\n const handleAddRemoteParticipant = () => {\n if (shouldPropagateMaster) {\n propagateMasterFunc();\n }\n };\n\n const isConnected = () => {\n return room._isDataChannelOpen && room.isConnected;\n };\n\n const parseDataEvents = function () {\n let msg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (msg.videoroom === 'sync_source_set' && msg.wt_channel_id != \"\") {\n emitter.emit('changePlayerSource', msg.wt_channel_id);\n } else if (msg.videoroom === 'sync_request') {\n roomSyncSend(msg.sync_slave_id).catch(() => {});\n }\n };\n\n const getCurrentSegmentPosition = () => {\n let position = _playerInterface.currentTime;\n return isNaN(position) ? 0 : position * 1000;\n };\n\n const calculateSyncDifferenceTime = (fragmentSn, fragmentPos, ping) => {\n let seekRanges = _playerInterface.buffered;\n let position = (fragmentPos + ping / 2) / 1000;\n\n if (seekRanges && !_playerInterfaceOptions.ignoreBufferedTimeRanges) {\n let seekRange = {};\n\n for (let i = 0; i < seekRanges.length; i++) {\n let _c_start = seekRanges.start(i);\n\n let _c_end = seekRanges.end(i);\n\n if (!seekRange.start || _c_start < seekRange.start) {\n seekRange.start = _c_start;\n }\n\n if (!seekRange.end || _c_end > seekRange.end) {\n seekRange.end = _c_end;\n }\n }\n\n if (position > seekRange.start && position < seekRange.end) {\n return {\n position,\n realPosition: position,\n isBufferSufficient: true\n };\n } else if (position < seekRange.start) {\n room._log(\"Syncing to \".concat(seekRange.start, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.start + 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else if (position > seekRange.end) {\n room._log(\"Syncing to \".concat(seekRange.end, \" instead of \").concat(position, \" due to lack of buffered data\"));\n\n return {\n position: seekRange.end - 0.5,\n realPosition: position,\n isBufferSufficient: false\n };\n } else return {\n position: null,\n isBufferSufficient: false\n };\n } else {\n return {\n position,\n realPosition: position,\n isBufferSufficient: true\n };\n }\n };\n\n const seekTo = time => {\n return new Promise((resolve, reject) => {\n if (_playerInterface.currentTime !== time) {\n let diff = time - _playerInterface.currentTime;\n\n if (_playerInterface.currentTime < time && diff < 4) {\n room._log(\"Fast forward to seek...\");\n\n let wasPaused = false;\n\n let _handleFailed = () => {\n if (wasPaused) {\n _playerInterface.pause();\n }\n\n _playerInterface.setPlaybackRate(1);\n\n isProgrammaticallySeeked = false;\n reject('Stalled');\n };\n\n let _fastForward = () => {\n isProgrammaticallySeeked = true;\n Object(_wt_utils__WEBPACK_IMPORTED_MODULE_0__[\"setExactTimeout\"])(() => {\n _emitter.off('stalled', _handleFailed);\n\n if (wasPaused && !_playerInterfaceOptions.isLive) {\n _playerInterface.pause();\n }\n\n _playerInterface.setPlaybackRate(1);\n\n isProgrammaticallySeeked = false;\n resolve();\n }, diff * 1000 / (playbackRate - 1), 20);\n\n _playerInterface.setPlaybackRate(playbackRate);\n };\n\n _emitter.once('stalled', _handleFailed);\n\n if (_playerInterface.isPaused) {\n wasPaused = true;\n\n _playerInterface.play().then(() => {\n _fastForward();\n }).catch(_handleFailed);\n } else {\n _fastForward();\n }\n } else {\n room._log(\"Jump to seek...\");\n\n let currentTimestamp = Date.now();\n let __failsafeId = null;\n\n let _resolve = () => {\n clearTimeout(__failsafeId);\n __failsafeId = null;\n isProgrammaticallySeeked = false;\n\n room._log(\"It took the player \".concat((Date.now() - currentTimestamp) / 1000, \" seconds to seek \"));\n\n resolve();\n };\n\n isProgrammaticallySeeked = true; // we need to ignore stall events since those are false alarm\n\n _emitter.once('playing', _resolve);\n\n __failsafeId = setTimeout(_resolve, waitForPlayingEventAfterSeek);\n\n _playerInterface.seek(time);\n }\n } else resolve();\n });\n };\n\n const handlePlaying = () => {\n if (!isProgrammaticallySeeked) {\n room._log('Handle playing');\n\n startSyncLoop();\n }\n\n isProgrammaticallySeeked = false;\n isPreloading = _playerInterface.isPaused;\n isPlaying = !_playerInterface.isPaused;\n };\n\n const handlePause = () => {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPlaying = !_playerInterface.isPaused;\n };\n\n const handleBuffering = () => {\n room._log('handleBuffering');\n\n if (!isProgrammaticallySeeked) {\n stopSyncLoop();\n clientPaused().catch(() => {});\n isPreloading = _playerInterface.isPaused;\n }\n };\n\n const roomSyncSend = slaveId => {\n if (!_playerInterface) {\n room._log(\"I've been asked for position even if we don't have player attached.\\n Does it mean I'm the master?\");\n\n return Promise.resolve();\n }\n\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n\n room._log(\"Sending my position to \".concat(slaveId));\n\n room._log(\"Current time: \".concat(fragmentPosition));\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_response\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition),\n slave_id: room.webrtcVersion > 1000 ? String(slaveId) : Number(slaveId)\n }\n });\n };\n\n const getSyncData = () => {\n room._log('Sending roomSync request');\n\n let roomId = room.roomId;\n let fragmentPosition = parseInt(getCurrentSegmentPosition());\n return new Promise((resolve, reject) => {\n let now = new Date().getTime();\n let ping = null;\n let sid = setTimeout(() => {\n room.off('data', fn, this);\n reject('Timeout');\n }, 3000);\n let body = {\n request: \"sync\",\n room: roomId,\n timestamp: new Date().getTime(),\n fragment: String(\"0\"),\n fragment_pos: Number(fragmentPosition)\n };\n\n let fn = msg => {\n if (msg.videoroom && ['sync', 'sync_response'].includes(msg.videoroom)) {\n if (msg.sync_master_await) {\n room._log('Waiting for master position');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n } else if (msg.sync_master_fragment || msg.sync_master_fragment_pos) {\n room._log('Got master position data');\n\n if (!ping) {\n ping = new Date().getTime() - now;\n }\n\n room._log(\"I'm master: \".concat(!!msg.sync_master_self));\n\n room._log(\"Ping: \".concat(ping));\n\n room._log(\"Master fragment: \".concat(msg.sync_master_fragment));\n\n room._log(\"Master fragment position: \".concat(msg.sync_master_fragment_pos));\n\n room.off('data', fn, this);\n clearTimeout(sid);\n resolve({\n isMaster: !!msg.sync_master_self,\n ping: ping,\n masterFragmentPos: parseInt(msg.sync_master_fragment_pos),\n masterFragmentSn: parseInt(msg.sync_master_fragment)\n });\n } else {\n clearTimeout(sid);\n reject('Master lost connection');\n }\n }\n };\n\n room.on('data', fn, this);\n room.sendMessage(room.handleId, {\n body\n }).then(fn).catch(e => {\n room.off('data', fn, this);\n clearTimeout(sid);\n reject(e);\n });\n });\n };\n\n const clientPaused = () => {\n room._log('Sending client paused');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_paused\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n fragment: '0',\n fragment_pos: 0\n }\n });\n };\n\n const propagateMasterFunc = () => {\n room._log('Propagating master');\n\n if (!isConnected()) {\n return Promise.resolve();\n }\n\n return room.sendMessage(room.handleId, {\n body: {\n request: \"sync_source_set\",\n room: room.roomId,\n timestamp: new Date().getTime(),\n wt_channel_id: \"\",\n fragment: \"0\",\n fragment_pos: 0\n }\n });\n };\n\n return {\n __events: ['playerSyncing'],\n getHandlers: () => ({\n handlePause: event => {\n room._log('handlePause');\n\n _emitter.emit('pause', event);\n },\n handlePlaying: event => {\n room._log('handlePlaying');\n\n _emitter.emit('playing', event);\n },\n handleBuffering: event => {\n room._log('handleBuffering');\n\n _emitter.emit('buffering', event);\n },\n handleStalled: event => {\n room._log('handleBuffering');\n\n room._log('handleStalled');\n\n _emitter.emit('buffering', event);\n\n _emitter.emit('stalled', event);\n },\n handleTimeupdate: event => {\n room._log('handleTimeupdate');\n\n _emitter.emit('timeupdate', event);\n }\n }),\n initialize: function () {\n let {\n playerInterface\n } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _playerInterface = playerInterface;\n _playerInterfaceOptions = { ..._playerInterfaceOptions,\n ...(_playerInterface.options || {})\n };\n\n room._log('Interface options passed: ', _playerInterface.options || {});\n\n room._log('All interface options: ', _playerInterfaceOptions);\n\n shouldPropagateMaster = false;\n isPlaying = _playerInterface.isPaused === false;\n room.on('disconnect', stopSyncLoop);\n room.on('removeLocalParticipant', stopSyncLoop);\n room.on('addLocalParticipant', handleAddLocalParticipant);\n room.on('addRemoteParticipant', handleAddRemoteParticipant);\n room.on('data', parseDataEvents);\n\n if (shouldPropagateMaster) {\n propagateMasterFunc().catch(() => {});\n }\n\n if (_playerInterface.isPaused === false) {\n _emitter.once('timeupdate', restartSyncLoop);\n }\n\n _emitter.on('buffering', handleBuffering);\n\n _emitter.on('playing', handlePlaying);\n\n _emitter.on('pause', handlePause);\n },\n destroy: () => {\n var _playerInterface2;\n\n stopSyncLoop();\n room.off('disconnect', stopSyncLoop);\n room.off('removeLocalParticipant', stopSyncLoop);\n room.off('addLocalParticipant', handleAddLocalParticipant);\n room.off('addRemoteParticipant', handleAddRemoteParticipant);\n room.off('data', parseDataEvents);\n\n if (typeof ((_playerInterface2 = _playerInterface) === null || _playerInterface2 === void 0 ? void 0 : _playerInterface2.destroy) === 'function') {\n var _playerInterface3;\n\n (_playerInterface3 = _playerInterface) === null || _playerInterface3 === void 0 ? void 0 : _playerInterface3.destroy();\n }\n\n _playerInterface = null;\n\n _emitter.clear();\n }\n };\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (syncUniversal);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/sync-modules/sync-universal.js?");
|
|
9336
9336
|
|
|
9337
9337
|
/***/ }),
|
|
9338
9338
|
|
|
@@ -9380,7 +9380,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */
|
|
|
9380
9380
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9381
9381
|
|
|
9382
9382
|
"use strict";
|
|
9383
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! aws-iot-device-sdk */ \"./node_modules/aws-iot-device-sdk/index.js\");\n/* harmony import */ var aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./wt-emitter */ \"./src/modules/wt-emitter.js\");\n\n\n\nclass Iot {\n constructor(enableDebugFlag) {\n Object.assign(this, Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])());\n this.device = null;\n this.decoder = new TextDecoder('utf-8');\n this.connectionActive = false;\n this.log = Iot.noop;\n this.debugFlag = enableDebugFlag;\n this.credentialsExpirationCheckIntervalId = null;\n this.currentCredentialsExpirationStamp = null;\n\n if (enableDebugFlag) {\n this.enableDebug();\n }\n }\n\n static noop() {}\n\n enableDebug() {\n this.log = console.log.bind(console);\n }\n\n startCredentialsExpirationCheck(expiration) {\n this.stopCredentialsExpirationCheck();\n this.currentCredentialsExpirationStamp = new Date(expiration).getTime();\n this.credentialsExpirationCheckIntervalId = setInterval(() => {\n const curentTimeStamp = new Date().getTime();\n\n if (this.currentCredentialsExpirationStamp - curentTimeStamp <= 300000) {\n this.emit('updateCredentials');\n }\n }, 5000);\n }\n\n stopCredentialsExpirationCheck() {\n clearInterval(this.credentialsExpirationCheckIntervalId);\n this.credentialsExpirationCheckIntervalId = null;\n }\n\n updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken, expiration) {\n if (this.device) {\n this.device.updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken);\n this.startCredentialsExpirationCheck(expiration);\n }\n }\n\n connect(apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration) {\n let forceDisconnect = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;\n return this.disconnect(forceDisconnect).then(() => {\n return new Promise((resolve, reject) => {\n this.device = Object(aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0__[\"device\"])({\n protocol: 'wss',\n clientId: apiMqttClientId,\n region,\n host: apiMqttUrl,\n accessKeyId: accessKeyId,\n secretKey: secretAccessKey,\n sessionToken: sessionToken,\n keepalive: 15,\n maximumReconnectTimeMs: 8000,\n enableMetrics: false,\n debug: this.debugFlag,\n autoResubscribe: true\n });\n this.startCredentialsExpirationCheck(expiration);\n\n let __s = () => {\n this.device.off('connect', __s);\n this.device.off('error', __e);\n resolve(this.device);\n };\n\n let __e = e => {\n this.device.off('connect', __s);\n this.device.off('error', __e);\n reject(e);\n };\n\n this.device.once('connect', __s);\n this.device.once('error', __e);\n this.device.on('message', this.__messageCb.bind(this));\n this.device.on('connect', this.__connectCb.bind(this));\n this.device.on('reconnect', this.__reconnectCb.bind(this));\n this.device.on('error', this.__failureCb.bind(this));\n this.device.on('close', this.__closeCb.bind(this));\n this.device.on('offline', this.__offlineCb.bind(this));\n });\n });\n }\n\n disconnect() {\n let force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n this.stopCredentialsExpirationCheck();\n return new Promise((resolve, reject) => {\n if (!this.device) {\n resolve();\n return;\n }\n\n let __i = null;\n\n let __c = () => {\n clearTimeout(__i);\n this.device = null;\n resolve();\n };\n\n __i = setTimeout(__c, 4000);\n this.device.off('message', this.__messageCb.bind(this));\n this.device.off('connect', this.__connectCb.bind(this));\n this.device.off('reconnect', this.__reconnectCb.bind(this));\n this.device.off('error', this.__failureCb.bind(this));\n this.device.off('close', this.__closeCb.bind(this));\n this.device.off('offline', this.__offlineCb.bind(this));\n this.device.end(force, __c);\n });\n }\n\n isConnected() {\n return this.connectionActive;\n }\n\n subscribe(topic) {\n return this.device && this.device.subscribe(topic);\n }\n\n unsubscribe(topic) {\n return this.device && this.device.unsubscribe(topic);\n }\n\n send(topic, message) {\n let msg = typeof message === 'object' ? JSON.stringify(message) : message;\n return this.device && this.device.publish(topic, msg);\n }\n\n __reconnectCb() {\n this.emit('reconnect');\n }\n\n __connectCb() {\n this.connectionActive = true;\n this.emit('connect');\n }\n\n __failureCb(err) {\n this.emit('error', err);\n }\n\n __closeCb(responseObject) {\n this.connectionActive = false;\n this.emit('close');\n }\n\n __offlineCb(responseObject) {\n this.emit('offline');\n }\n\n __messageCb(t, message, packet) {\n const topic = t.split('/');\n
|
|
9383
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! aws-iot-device-sdk */ \"./node_modules/aws-iot-device-sdk/index.js\");\n/* harmony import */ var aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./wt-emitter */ \"./src/modules/wt-emitter.js\");\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./wt-utils */ \"./src/modules/wt-utils.js\");\n\n\n\n\nclass Iot {\n constructor(enableDebugFlag) {\n Object.assign(this, Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])());\n this.device = null;\n this.decoder = new TextDecoder('utf-8');\n this.connectionActive = false;\n this.log = Iot.noop;\n this.debugFlag = enableDebugFlag;\n this.credentialsExpirationCheckIntervalId = null;\n this.currentCredentialsExpirationStamp = null;\n\n if (enableDebugFlag) {\n this.enableDebug();\n }\n }\n\n static noop() {}\n\n enableDebug() {\n this.log = console.log.bind(console);\n }\n\n startCredentialsExpirationCheck(expiration) {\n this.stopCredentialsExpirationCheck();\n this.currentCredentialsExpirationStamp = new Date(expiration).getTime();\n this.credentialsExpirationCheckIntervalId = setInterval(() => {\n const curentTimeStamp = new Date().getTime();\n\n if (this.currentCredentialsExpirationStamp - curentTimeStamp <= 300000) {\n this.emit('updateCredentials');\n }\n }, 5000);\n }\n\n stopCredentialsExpirationCheck() {\n clearInterval(this.credentialsExpirationCheckIntervalId);\n this.credentialsExpirationCheckIntervalId = null;\n }\n\n updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken, expiration) {\n if (this.device) {\n this.device.updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken);\n this.startCredentialsExpirationCheck(expiration);\n }\n }\n\n connect(apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration) {\n let forceDisconnect = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;\n return this.disconnect(forceDisconnect).then(() => {\n return new Promise((resolve, reject) => {\n this.device = Object(aws_iot_device_sdk__WEBPACK_IMPORTED_MODULE_0__[\"device\"])({\n protocol: 'wss',\n clientId: apiMqttClientId,\n region,\n host: apiMqttUrl,\n accessKeyId: accessKeyId,\n secretKey: secretAccessKey,\n sessionToken: sessionToken,\n keepalive: 15,\n maximumReconnectTimeMs: 8000,\n enableMetrics: false,\n debug: this.debugFlag,\n autoResubscribe: true\n });\n this.startCredentialsExpirationCheck(expiration);\n\n let __s = () => {\n var _this$device, _this$device2;\n\n (_this$device = this.device) === null || _this$device === void 0 ? void 0 : _this$device.off('connect', __s);\n (_this$device2 = this.device) === null || _this$device2 === void 0 ? void 0 : _this$device2.off('error', __e);\n resolve(this.device);\n };\n\n let __e = e => {\n var _this$device3, _this$device4;\n\n (_this$device3 = this.device) === null || _this$device3 === void 0 ? void 0 : _this$device3.off('connect', __s);\n (_this$device4 = this.device) === null || _this$device4 === void 0 ? void 0 : _this$device4.off('error', __e);\n reject(e);\n };\n\n this.device.once('connect', __s);\n this.device.once('error', __e);\n this.device.on('message', this.__messageCb.bind(this));\n this.device.on('connect', this.__connectCb.bind(this));\n this.device.on('reconnect', this.__reconnectCb.bind(this));\n this.device.on('error', this.__failureCb.bind(this));\n this.device.on('close', this.__closeCb.bind(this));\n this.device.on('offline', this.__offlineCb.bind(this));\n });\n });\n }\n\n disconnect() {\n let force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n this.stopCredentialsExpirationCheck();\n return new Promise((resolve, reject) => {\n if (!this.device) {\n resolve();\n return;\n }\n\n let __i = null;\n\n let __c = () => {\n clearTimeout(__i);\n this.device = null;\n resolve();\n };\n\n __i = setTimeout(__c, 4000);\n this.device.off('message', this.__messageCb.bind(this));\n this.device.off('connect', this.__connectCb.bind(this));\n this.device.off('reconnect', this.__reconnectCb.bind(this));\n this.device.off('error', this.__failureCb.bind(this));\n this.device.off('close', this.__closeCb.bind(this));\n this.device.off('offline', this.__offlineCb.bind(this));\n this.device.end(force, __c);\n });\n }\n\n isConnected() {\n return this.connectionActive;\n }\n\n subscribe(topic) {\n return this.device && this.device.subscribe(topic);\n }\n\n unsubscribe(topic) {\n return this.device && this.device.unsubscribe(topic);\n }\n\n send(topic, message) {\n let msg = typeof message === 'object' ? JSON.stringify(message) : message;\n return this.device && this.device.publish(topic, msg);\n }\n\n __reconnectCb() {\n this.emit('reconnect');\n }\n\n __connectCb() {\n this.connectionActive = true;\n this.emit('connect');\n }\n\n __failureCb(err) {\n this.emit('error', err);\n }\n\n __closeCb(responseObject) {\n this.connectionActive = false;\n this.emit('close');\n }\n\n __offlineCb(responseObject) {\n this.emit('offline');\n }\n\n __messageCb(t, message, packet) {\n const topic = t.split('/');\n let payload = JSON.parse(this.decoder.decode(message));\n\n if (payload.display) {\n const decodedDisplay = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(payload.display);\n\n if (decodedDisplay.userId) {\n payload = { ...payload,\n userId: decodedDisplay.userId\n };\n }\n }\n\n if (topic[0] === 'user') {\n // user\n const userId = topic[1].replace(\"_\", ':');\n this.emit('message', {\n userId,\n ...payload,\n event: payload.event ? \"user:\".concat(payload.event) : 'user'\n });\n } else if (topic[0] === 'wt') {\n const event = payload.event;\n const roomId = topic[2];\n\n if (topic[1] === 'room') {\n // room\n if (event === 'message' || event === 'template_updated' || event === 'record_start' || event === 'record_stop' || event === 'record_configured' || event === 'record_livestream_available' || event === 'record_livestream_kick' || event === 'user_update_displayname' || event === 'user_update_avatar' || event === 'user_update_customattributes' || event === 'user_update_privateattributes' || event === 'channel_changed' || event === \"instance_homepage_changed\" || event === \"instance_settings_changed\" || event === \"externalmix_changed\" || event === \"video_uploaded\" || event === \"change_user_devices\" || event === \"queue\" || event === \"title_changed\") {\n this.emit('message', {\n event,\n ...payload,\n roomId\n });\n } else if (event === 'joined' || event === 'leaving') {\n this.emit('message', {\n event,\n ...payload,\n isObserver: !!payload.isObserver,\n roomId\n });\n } else if (event === 'left' || //user removed room a.k.a. left the room\n event === 'kicked' || event === 'banned' || event === 'unbanned' || event === 'approved' || event === 'muted' || event === 'unmuted' || event === 'messageRemoved' || event === 'messageReported' || event === 'chatClear' || event === 'handRaised' || event === 'handLowered' || event === 'handsCleared') {\n this.emit('message', {\n event,\n ...payload\n });\n } else if (event === 'volume_set') {\n this.emit('message', {\n event,\n ...payload\n });\n }\n } else if (topic[1] === 'instanceroom') {\n // instance\n if (event === 'add_room' || event === 'remove_room' || event === 'set_room' || event === \"instance_homepage_changed\" || event === 'instance_settings_changed') {\n this.emit('message', {\n event,\n ...payload\n });\n }\n } else if (topic[1] === 'externalmix') {\n const event = payload.event;\n this.emit('message', {\n event,\n ...payload\n });\n }\n } else if (topic[0] === 'wtr') {\n const recorderId = topic[1];\n const sessionId = topic[2];\n\n if (topic[3] === 'control') {\n this.emit('message', {\n event: 'recorder_control',\n ...payload,\n recorderId,\n sessionId\n });\n } // recorder control\n else if (topic[3] === 'monitor') {\n this.emit('message', {\n event: 'recorder_monitor',\n ...payload,\n recorderId,\n sessionId\n });\n } // recorder monitor\n\n }\n }\n\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Iot);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/wt-iot.js?");
|
|
9384
9384
|
|
|
9385
9385
|
/***/ }),
|
|
9386
9386
|
|
|
@@ -9392,7 +9392,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var aws_
|
|
|
9392
9392
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9393
9393
|
|
|
9394
9394
|
"use strict";
|
|
9395
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webrtc-adapter */ \"./node_modules/webrtc-adapter/src/js/adapter_core.js\");\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./wt-emitter */ \"./src/modules/wt-emitter.js\");\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./wt-utils */ \"./src/modules/wt-utils.js\");\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n// Watch together janus webrtc library\n\n\n\n\nclass Room {\n constructor(debug) {\n this.debug = debug;\n this.sessions = [];\n this.safariVp8 = false;\n this.browser = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser;\n this.browserDetails = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails;\n this.webrtcSupported = Room.isWebrtcSupported();\n this.safariVp8TestPromise = Room.testSafariVp8();\n this.safariVp8 = null;\n this.safariVp8TestPromise.then(safariVp8 => {\n this.safariVp8 = safariVp8;\n }); // Let's get it started\n\n this.whenInitialized = this.initialize();\n }\n\n initialize() {\n return this.safariVp8TestPromise.then(() => this);\n }\n\n createSession() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return new RoomSession(constructId, type, {\n debug: this.debug,\n ...options\n });\n }\n\n static testSafariVp8() {\n return new Promise(resolve => {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'safari' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 605) {\n if (RTCRtpSender && RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities(\"video\") && RTCRtpSender.getCapabilities(\"video\").codecs && RTCRtpSender.getCapabilities(\"video\").codecs.length) {\n var isVp8 = false;\n\n for (var i in RTCRtpSender.getCapabilities(\"video\").codecs) {\n var codec = RTCRtpSender.getCapabilities(\"video\").codecs[i];\n\n if (codec && codec.mimeType && codec.mimeType.toLowerCase() === \"video/vp8\") {\n isVp8 = true;\n break;\n }\n }\n\n resolve(isVp8);\n } else {\n // We do it in a very ugly way, as there's no alternative...\n // We create a PeerConnection to see if VP8 is in an offer\n var testpc = new RTCPeerConnection({}, {});\n testpc.createOffer({\n offerToReceiveVideo: true\n }).then(function (offer) {\n let result = offer.sdp.indexOf(\"VP8\") !== -1;\n testpc.close();\n testpc = null;\n resolve(result);\n });\n }\n } else resolve(false);\n });\n }\n\n static isWebrtcSupported() {\n return window.RTCPeerConnection !== undefined && window.RTCPeerConnection !== null && navigator.mediaDevices !== undefined && navigator.mediaDevices !== null && navigator.mediaDevices.getUserMedia !== undefined && navigator.mediaDevices.getUserMedia !== null;\n }\n\n}\n\nclass RoomSession {\n static noop() {}\n\n static randomString(len) {\n var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n var randomString = '';\n\n for (var i = 0; i < len; i++) {\n var randomPoz = Math.floor(Math.random() * charSet.length);\n randomString += charSet.substring(randomPoz, randomPoz + 1);\n }\n\n return randomString;\n } //TODO: solve\n // #eventList = ['error', 'kicked', 'addLocalParticipant', ,'addRemoteInstructor','addRemoteParticipant','addRemoteTalkback', 'addRemoteObserver', 'removeRemoteInstructor', 'removeLocalParticipant', 'removeRemoteParticipant', 'removeRemoteTalkback', 'removeRemoteObserver', 'localMuted', 'localHasVideo', 'localHasAudio', 'data', 'iceState', 'connectionState', 'joined', 'joining', 'dataChannel', 'disconnect', 'observerIds', 'talkbackIds', 'instructorId', 'published', 'publishing', 'remoteTrackMuted', 'streamingStatus', 'streaming', 'streamStarting'];\n //\n\n\n constructor() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n Object.assign(this, Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])());\n this.defaultDataChannelLabel = 'JanusDataChannel';\n this.server = null;\n this.iceServers = null;\n this.token = null;\n this.roomId = null;\n this.streamId = null;\n this.pin = null;\n this.userId = null;\n this.sessiontype = type;\n this.initialBitrate = 0; //TODO: remove this\n\n this.isMonitor = false; // currently used just for classroom context so monitor user only subscribes to participants and not trainer (for other monitor this flag is not necessary)\n\n this.recordingFilename = null;\n this.pluginName = RoomSession.sessionTypes[type];\n this.options = options;\n this.id = null;\n this.privateId = null;\n this.constructId = constructId || RoomSession.randomString(16);\n this.sessionId = null;\n this.handleId = null;\n this.ws = null;\n this.isRestarting = false; //TODO: do it better\n // double click prevention\n\n this.connectingPromise = null;\n this.disconnectingPromise = null;\n this._ipv6Support = false;\n this._retries = 0;\n this._maxRetries = 3;\n this._keepAliveId = null;\n this._participants = [];\n this._observerIds = [];\n this._talkbackIds = [];\n this._instuctorId = null;\n this._hasJoined = false;\n this._isStreaming = false;\n this._isPublished = false;\n this._isDataChannelOpen = false;\n this._dataChannelTimeoutId = null;\n this._messageTimeoutId = null;\n this.isAudioMuted = false;\n this.isVideoMuted = false;\n this.isVideoEnabled = false;\n this.isAudioEnabed = false;\n this.isUnifiedPlan = RoomSession.checkUnifiedPlan();\n this.subscriptionRules = { ...RoomSession.subscriptionRules,\n ...(this.options.subscriptionRules || {})\n };\n this._log = RoomSession.noop;\n\n if (this.options.debug) {\n this._enableDebug();\n }\n } // Check if this browser supports Unified Plan and transceivers\n // Based on https://codepen.io/anon/pen/ZqLwWV?editors=0010\n\n\n static checkUnifiedPlan() {\n let unifiedPlan = false;\n\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'firefox' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 59) {\n // Firefox definitely does, starting from version 59\n unifiedPlan = true;\n } else if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'chrome' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 72) {\n // Chrome does, but it's only usable from version 72 on\n unifiedPlan = true;\n } else if (!window.RTCRtpTransceiver || !('currentDirection' in RTCRtpTransceiver.prototype)) {\n // Safari supports addTransceiver() but not Unified Plan when\n // currentDirection is not defined (see codepen above).\n unifiedPlan = false;\n } else {\n // Check if addTransceiver() throws an exception\n const tempPc = new RTCPeerConnection();\n\n try {\n tempPc.addTransceiver('audio');\n unifiedPlan = true;\n } catch (e) {}\n\n tempPc.close();\n }\n\n return unifiedPlan;\n }\n\n _participantShouldSubscribe(userId) {\n let allowedObservers = this._observerIds || [];\n let allowedTalkback = this._talkbackIds || [];\n let allowedInstructor = this._instuctorId || null;\n let localUserRole = 'participant';\n\n if (this.isMonitor) {\n localUserRole = 'monitor';\n } else if (allowedObservers.indexOf(this.userId) > -1) {\n localUserRole = 'observer';\n } else if (allowedTalkback.indexOf(this.userId) > -1) {\n localUserRole = 'talkback';\n } else if (this.userId === allowedInstructor) {\n localUserRole = 'instructor';\n }\n\n let remoteUserRole = 'participant';\n\n if (allowedObservers.indexOf(userId) > -1) {\n remoteUserRole = 'observer';\n } else if (allowedTalkback.indexOf(userId) > -1) {\n remoteUserRole = 'talkback';\n } else if (userId === allowedInstructor) {\n remoteUserRole = 'instructor';\n }\n\n let mode = allowedInstructor !== null ? 'videoWall' : 'watchTogether';\n return this.subscriptionRules[localUserRole][mode].indexOf(remoteUserRole) > -1;\n }\n\n _getAddParticipantEventName(handleId) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n }\n\n let allowedTalkback = this._talkbackIds || [];\n let allowedObservers = this._observerIds || [];\n let allowedInstructor = this._instuctorId || null;\n let eventName = 'addRemoteParticipant';\n\n if (handle.userId === allowedInstructor) {\n eventName = 'addRemoteInstructor';\n }\n\n if (allowedTalkback.indexOf(handle.userId) > -1) {\n eventName = 'addRemoteTalkback';\n }\n\n if (allowedObservers.indexOf(handle.userId) > -1) {\n eventName = 'addRemoteObserver';\n }\n\n return eventName;\n }\n\n _getRemoveParticipantEventName(handleId) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n }\n\n let allowedTalkback = this._talkbackIds || [];\n let allowedObservers = this._observerIds || [];\n let allowedInstructor = this._instuctorId || null;\n let eventName = 'removeRemoteParticipant';\n\n if (handle.userId === allowedInstructor) {\n eventName = 'removeRemoteInstructor';\n }\n\n if (allowedTalkback.indexOf(handle.userId) > -1) {\n eventName = 'removeRemoteTalkback';\n }\n\n if (allowedObservers.indexOf(handle.userId) > -1) {\n eventName = 'removeRemoteObserver';\n }\n\n return eventName;\n }\n\n sendMessage(handleId) {\n let message = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n body: 'Example Body'\n };\n let dontWait = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let dontResolveOnAck = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n return this._send({\n \"janus\": \"message\",\n \"handle_id\": handleId,\n ...message\n }, dontWait, dontResolveOnAck).then(json => {\n if (json && json[\"janus\"] === \"success\") {\n let plugindata = json[\"plugindata\"] || {};\n let data = plugindata[\"data\"];\n return Promise.resolve(data);\n }\n\n return Promise.resolve();\n }).catch(json => {\n if (json && json[\"error\"]) {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json[\"error\"]\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json\n });\n }\n });\n }\n\n _send() {\n let request = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let ignoreResponse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n let dontResolveOnAck = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let transaction = RoomSession.randomString(12);\n let requestData = { ...request,\n transaction,\n token: this.token,\n ...(this.sessionId && {\n 'session_id': this.sessionId\n } || {})\n };\n return new Promise((resolve, reject) => {\n let parseResponse = event => {\n let json = JSON.parse(event.data);\n let r_transaction = json['transaction'];\n\n if (r_transaction === transaction && (!dontResolveOnAck || json['janus'] !== 'ack')) {\n clearTimeout(this._messageTimeoutId);\n this.ws.removeEventListener('message', parseResponse);\n\n if (json['janus'] === 'error') {\n var _json$error;\n\n if ((json === null || json === void 0 ? void 0 : (_json$error = json.error) === null || _json$error === void 0 ? void 0 : _json$error.code) == 403) {\n this.disconnect(true);\n }\n\n reject({\n type: 'error',\n id: 2,\n message: 'send failed',\n data: json,\n requestData\n });\n } else {\n resolve(json);\n }\n }\n };\n\n if (ignoreResponse) {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.send(JSON.stringify(requestData));\n }\n\n resolve();\n } else {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.addEventListener('message', parseResponse);\n this._messageTimeoutId = setTimeout(() => {\n this.ws.removeEventListener('message', parseResponse);\n reject({\n type: 'error',\n id: 3,\n message: 'send timeout',\n data: requestData\n });\n }, 10000);\n this.ws.send(JSON.stringify(requestData));\n } else {\n reject({\n type: 'warning',\n id: 29,\n message: 'No connection to WebSockets',\n data: requestData\n });\n }\n }\n });\n }\n\n _connectionClosed() {\n if (this.disconnectingPromise || this.connectingPromise) {\n return;\n }\n\n if (this._retries < this._maxRetries) {\n setTimeout(() => {\n this._retries++;\n\n this._reconnect().catch(e => {\n this.emit('error', e);\n });\n }, 3000 * this._retries);\n } else {\n if (this.sessiontype === 'reactooroom') {\n this.disconnect(true);\n } else if (this.sessiontype === 'streaming') {\n this.stopStream();\n }\n\n this.emit('error', {\n type: 'error',\n id: 4,\n message: 'Lost connection to WebSockets',\n data: null\n });\n }\n }\n\n _wipeListeners() {\n if (this.ws) {\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n }\n }\n\n _startKeepAlive() {\n this._send({\n \"janus\": \"keepalive\"\n }).then(json => {\n if (json[\"janus\"] !== 'ack') {\n this.emit('error', {\n type: 'warning',\n id: 5,\n message: 'keepalive response suspicious',\n data: json[\"janus\"]\n });\n }\n }).catch(e => {\n this.emit('error', {\n type: 'warning',\n id: 6,\n message: 'keepalive dead',\n data: e\n });\n\n this._connectionClosed();\n });\n\n this._keepAliveId = setTimeout(() => {\n this._startKeepAlive();\n }, 30000);\n }\n\n _stopKeepAlive() {\n clearTimeout(this._keepAliveId);\n }\n\n _handleWsEvents(event) {\n let json = JSON.parse(event.data);\n var sender = json[\"sender\"];\n var type = json[\"janus\"];\n\n let handle = this._getHandle(sender);\n\n if (!handle) {\n return;\n }\n\n if (type === \"trickle\") {\n let candidate = json[\"candidate\"];\n let config = handle.webrtcStuff;\n\n if (config.pc && config.remoteSdp) {\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n } else {\n if (!config.candidates) {\n config.candidates = [];\n }\n\n config.candidates.push(candidate);\n }\n } else if (type === \"webrtcup\") {//none universal\n } else if (type === \"hangup\") {\n this._log('hangup on', handle.handleId);\n\n this._removeParticipant(handle.handleId, null, false);\n } else if (type === \"detached\") {\n this._log('detached on', handle.handleId);\n\n this._removeParticipant(handle.handleId, null, true);\n } else if (type === \"media\") {\n this._log('Media event:', handle.handleId, json[\"type\"], json[\"receiving\"], json[\"mid\"]);\n } else if (type === \"slowlink\") {\n this._log('Slowlink', handle.handleId, json[\"uplink\"], json[\"lost\"], json[\"mid\"]);\n } else if (type === \"event\") {//none universal\n } else if (type === 'timeout') {\n this.ws.close(3504, \"Gateway timeout\");\n } else if (type === 'success' || type === 'error') {// we're capturing those elsewhere\n } else {\n this._log(\"Unknown event: \".concat(type, \" on session: \").concat(this.sessionId));\n } // LOCAL\n\n\n if (sender === this.handleId) {\n if (type === \"event\") {\n var plugindata = json[\"plugindata\"] || {};\n var msg = plugindata[\"data\"] || {};\n var jsep = json[\"jsep\"];\n let result = msg[\"result\"] || null;\n let event = msg[\"videoroom\"] || null;\n let list = msg[\"publishers\"] || {};\n let leaving = msg[\"leaving\"]; //let joining = msg[\"joining\"];\n\n let unpublished = msg[\"unpublished\"];\n let error = msg[\"error\"];\n\n if (event === \"joined\") {\n this.id = msg[\"id\"];\n this.privateId = msg[\"private_id\"];\n this._hasJoined = true;\n this.emit('joined', true);\n\n this._log('We have successfully joined Room');\n\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n\n this._log('Remote userId: ', userId);\n\n if (this._participantShouldSubscribe(userId)) {\n this._log('Creating user: ', userId);\n\n this._createParticipant(userId, id).then(handle => {\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"feed\": id,\n \"private_id\": this.privateId,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (event === \"event\") {\n if (msg[\"streams\"] !== undefined && msg[\"streams\"] !== null) {\n this._log('Got my own streams back', msg[\"streams\"]);\n }\n\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n\n this._log('Remote userId: ', userId);\n\n if (this._participantShouldSubscribe(userId)) {\n this._log('Creating user: ', userId);\n\n this._createParticipant(userId, id).then(handle => {\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"feed\": id,\n \"private_id\": this.privateId,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n\n if (leaving === 'ok') {\n this._log('leaving', this.handleId, 'this is us');\n\n this._removeParticipant(this.handleId);\n\n if (msg['reason'] === 'kicked') {\n this.emit('kicked');\n this.disconnect().catch(() => {});\n }\n } else if (leaving) {\n this._log('leaving', leaving);\n\n this._removeParticipant(null, leaving);\n }\n\n if (unpublished === 'ok') {\n this._log('unpublished', this.handleId, 'this is us');\n\n this._removeParticipant(this.handleId, null, false); // we do just hangup\n\n } else if (unpublished) {\n this._log('unpublished', unpublished);\n\n this._removeParticipant(null, unpublished, true); // we do hangup and detach\n\n }\n\n if (error) {\n this.emit('error', {\n type: 'error',\n id: 7,\n message: 'local participant error',\n data: [sender, msg]\n });\n }\n } // Streaming related\n else if (result && result[\"status\"]) {\n this.emit('streamingStatus', result[\"status\"]);\n\n if (result[\"status\"] === 'stopped') {\n this.stopStream();\n }\n\n if (result[\"status\"] === 'started') {\n this.emit('streaming', true);\n this._isStreaming = true;\n }\n }\n\n if (jsep !== undefined && jsep !== null) {\n if (this.sessiontype === 'reactooroom') {\n this._webrtcPeer(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n } else if (this.sessiontype === 'streaming') {\n this._publishRemote(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (type === \"webrtcup\") {\n this._log('Configuring bitrate: ' + this.initialBitrate);\n\n if (this.initialBitrate > 0) {\n this.sendMessage(this.handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"bitrate\": this.initialBitrate\n }\n }).catch(() => null);\n }\n }\n } //REMOTE\n else {\n let plugindata = json[\"plugindata\"] || {};\n let msg = plugindata[\"data\"] || {};\n let jsep = json[\"jsep\"];\n let event = msg[\"videoroom\"];\n let error = msg[\"error\"];\n\n if (event === \"attached\") {\n this._log('Remote have successfully joined Room', msg);\n\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: handle.userId,\n stream: null,\n track: null,\n adding: false,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: false,\n hasVideoTrack: false\n });\n }\n\n if (error) {\n this.emit('error', {\n type: 'warning',\n id: 8,\n message: 'remote participant error',\n data: [sender, msg]\n });\n }\n\n if (jsep) {\n this._publishRemote(handle.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n }\n\n _handleDataEvents(handleId, type, data) {\n let handle = this._getHandle(handleId);\n\n if (type === 'state') {\n this._log(\" - Data channel status - \", \"UID: \".concat(handleId), \"STATUS: \".concat(JSON.stringify(data)), \"ME: \".concat(handleId === this.handleId));\n\n if (handle) {\n let config = handle.webrtcStuff;\n config.dataChannelOpen = this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label) && (data === null || data === void 0 ? void 0 : data.state) === 'open';\n }\n\n if (handleId === this.handleId && this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label)) {\n this._isDataChannelOpen = (data === null || data === void 0 ? void 0 : data.state) === 'open';\n this.emit('dataChannel', (data === null || data === void 0 ? void 0 : data.state) === 'open');\n }\n }\n\n if (type === 'error') {\n this.emit('error', {\n type: 'warning',\n id: 9,\n message: 'data event warning',\n data: [handleId, data]\n });\n\n if (handle) {\n let config = handle.webrtcStuff;\n\n if (this.defaultDataChannelLabel === data.label) {\n config.dataChannelOpen = false;\n }\n }\n\n if (handleId === this.handleId && this.defaultDataChannelLabel === data.label) {\n this._isDataChannelOpen = false;\n this.emit('dataChannel', false);\n }\n }\n\n if (handleId === this.handleId && type === 'message') {\n let d = null;\n\n try {\n d = JSON.parse(data);\n } catch (e) {\n this.emit('error', {\n type: 'warning',\n id: 10,\n message: 'data message parse error',\n data: [handleId, e]\n });\n return;\n }\n\n this.emit('data', d);\n }\n } //removeHandle === true -> hangup, detach, removeHandle === false -> hangup\n\n\n _removeParticipant(handleId, rfid) {\n let removeHandle = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n\n let handle = this._getHandle(handleId, rfid);\n\n if (!handle) {\n return Promise.resolve();\n } else {\n handleId = handle.handleId;\n }\n\n return this._send({\n \"janus\": \"hangup\",\n \"handle_id\": handleId\n }, true).then(() => removeHandle ? this._send({\n \"janus\": \"detach\",\n \"handle_id\": handleId\n }, true) : Promise.resolve()).finally(() => {\n try {\n if (handle.webrtcStuff.stream && !this.isRestarting) {\n handle.webrtcStuff.stream.getTracks().forEach(track => track.stop());\n }\n } catch (e) {// Do nothing\n }\n\n handle.webrtcStuff.stream = null;\n\n if (handle.webrtcStuff.dataChannel) {\n Object.keys(handle.webrtcStuff.dataChannel).forEach(label => {\n handle.webrtcStuff.dataChannel[label].onmessage = null;\n handle.webrtcStuff.dataChannel[label].onopen = null;\n handle.webrtcStuff.dataChannel[label].onclose = null;\n handle.webrtcStuff.dataChannel[label].onerror = null;\n });\n }\n\n if (handle.webrtcStuff.pc) {\n handle.webrtcStuff.pc.onicecandidate = null;\n handle.webrtcStuff.pc.ontrack = null;\n handle.webrtcStuff.pc.ondatachannel = null;\n handle.webrtcStuff.pc.onconnectionstatechange = null;\n handle.webrtcStuff.pc.oniceconnectionstatechange = null;\n }\n\n try {\n handle.webrtcStuff.pc.close();\n } catch (e) {}\n\n handle.webrtcStuff = {\n stream: null,\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false\n };\n\n if (handleId === this.handleId) {\n this._isDataChannelOpen = false;\n this._isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('removeLocalParticipant', {\n id: handleId,\n userId: handle.userId\n });\n } else {\n this.emit(this._getRemoveParticipantEventName(handleId), {\n id: handleId,\n userId: handle.userId\n });\n }\n\n if (removeHandle) {\n let handleIndex = this._participants.findIndex(p => p.handleId === handleId);\n\n this._participants.splice(handleIndex, 1);\n }\n\n return true;\n });\n }\n\n _createParticipant() {\n let userId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n return this._send({\n \"janus\": \"attach\",\n \"plugin\": this.pluginName\n }).then(json => {\n let handleId = json.data[\"id\"];\n let handle = {\n handleId,\n rfid,\n userId,\n webrtcStuff: {\n stream: null,\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n isIceRestarting: false\n }\n };\n\n this._participants.push(handle);\n\n return handle;\n });\n }\n\n _joinRoom(roomId, pin, userId, display) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": roomId,\n \"pin\": pin,\n \"ptype\": \"publisher\",\n \"display\": display,\n ...(this.webrtcVersion > 1000 ? {\n id: userId\n } : {})\n }\n }, false, true);\n }\n\n _watchStream(id) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"watch\",\n id: parseInt(id)\n }\n }, false, true);\n }\n\n _leaveRoom() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this._hasJoined ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"leave\"\n }\n }, dontWait).finally(() => {\n this._hasJoined = false;\n this.emit('joined', false);\n }) : Promise.resolve();\n } // internal reconnect\n\n\n _reconnect() {\n if (this.connectingPromise) {\n return this.connectingPromise;\n }\n\n if (this.ws) {\n this._wipeListeners();\n\n if (this.ws.readyState === 1) {\n this.ws.close();\n }\n }\n\n this._stopKeepAlive();\n\n this.connectingPromise = new Promise((resolve, reject) => {\n this.emit('joining', true);\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n\n this.ws.onopen = () => {\n this._send({\n \"janus\": \"claim\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n this._startKeepAlive();\n\n this.connectingPromise = null;\n this.emit('joining', false);\n this._retries = 0;\n resolve(json);\n }).catch(error => {\n this.connectingPromise = null;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 11,\n message: 'reconnection error',\n data: error\n });\n });\n }; // this is called before 'close' event callback so it doesn't break reconnect loop\n\n\n this.ws.onerror = e => {\n this.connectingPromise = null;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 12,\n message: 'ws reconnection error',\n data: e\n });\n };\n });\n return this.connectingPromise;\n }\n\n connect(roomId, pin, server, iceServers, token, display, userId) {\n let webrtcVersion = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 0;\n let initialBitrate = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : 0;\n let isMonitor = arguments.length > 9 ? arguments[9] : undefined;\n let recordingFilename = arguments.length > 10 ? arguments[10] : undefined;\n\n if (this.connectingPromise) {\n return this.connectingPromise;\n }\n\n this.emit('joined', false);\n\n if (this.ws) {\n this._wipeListeners();\n }\n\n this._stopKeepAlive();\n\n this.disconnectingPromise = null;\n this.sessionId = null;\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.roomId = roomId;\n this.pin = pin;\n this.display = display;\n this.userId = userId;\n this.webrtcVersion = webrtcVersion;\n this.initialBitrate = initialBitrate;\n this.isMonitor = isMonitor;\n this.recordingFilename = recordingFilename;\n this.disconnectingPromise = null;\n this.connectingPromise = new Promise((resolve, reject) => {\n this.emit('joining', true);\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n\n this.ws.onopen = () => {\n this._retries = 0;\n\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n this._startKeepAlive();\n\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._joinRoom(roomId, pin, userId, display)).then(() => {\n this.connectingPromise = null;\n this.emit('joining', false);\n resolve(this);\n }).catch(error => {\n this.connectingPromise = null;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n\n this.ws.onerror = e => {\n this.connectingPromise = null;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n });\n return this.connectingPromise;\n }\n\n disconnect() {\n if (this.disconnectingPromise) {\n return this.disconnectingPromise;\n }\n\n let _hasJoined = this._hasJoined;\n clearTimeout(this._messageTimeoutId);\n clearTimeout(this._dataChannelTimeoutId);\n\n this._stopKeepAlive();\n\n this.disconnectingPromise = Promise.all(this._participants.map(p => this._removeParticipant(p.handleId))).finally(() => {\n this._wipeListeners();\n\n if (this.ws && this.ws.readyState === 1) {\n this._send({\n \"janus\": \"destroy\"\n }, true);\n\n this.ws.close();\n }\n\n this.sessionId = null; //TODO: Just in case something crashed along the way\n\n this._isPublished = false;\n this._hasJoined = false;\n this.emit('publishing', false);\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('joining', false);\n this.emit('joined', false);\n this.emit('disconnect', _hasJoined);\n return Promise.resolve('Disconnected');\n });\n return this.disconnectingPromise;\n }\n\n startStream(streamId, server, iceServers, token, userId) {\n if (this.connectingPromise) {\n return this.connectingPromise;\n }\n\n this.emit('streaming', false);\n\n if (this.ws) {\n this._wipeListeners();\n }\n\n this._stopKeepAlive();\n\n this.disconnectingPromise = null;\n this.sessionId = null;\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.streamId = streamId;\n this.userId = userId;\n this.connectingPromise = new Promise((resolve, reject) => {\n this.emit('streamStarting', true);\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n\n this.ws.onopen = () => {\n this._retries = 0;\n\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n this._startKeepAlive();\n\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._watchStream(streamId)).then(() => {\n this.connectingPromise = null;\n this.emit('streamStarting', false);\n resolve(this);\n }).catch(error => {\n this.connectingPromise = null;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n\n this.ws.onerror = e => {\n this.connectingPromise = null;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n });\n return this.connectingPromise;\n }\n\n stopStream() {\n if (this.disconnectingPromise) {\n return this.disconnectingPromise;\n }\n\n let _isStreaming = this._isStreaming;\n\n this._stopKeepAlive();\n\n this.disconnectingPromise = this.sendMessage(this.handleId, {\n body: {\n \"request\": \"stop\"\n }\n }, false, true).then(() => this._removeParticipant(this.handleId)).finally(() => {\n this._wipeListeners();\n\n this._send({\n \"janus\": \"destroy\"\n }, true);\n\n if (this.ws && this.ws.readyState === 1) {\n this.ws.close();\n }\n\n this.sessionId = null;\n this._isStreaming = false;\n this.emit('streamStarting', false);\n this.emit('streaming', false);\n this.emit('disconnect', _isStreaming);\n this.disconnectingPromise = null;\n return Promise.resolve('Disconnected');\n });\n return this.disconnectingPromise;\n }\n\n destroy() {\n if (this.sessiontype === 'reactooroom') {\n return this.disconnect().then(() => {\n this.clear();\n return true;\n });\n } else if (this.sessiontype === 'streaming') {\n return this.stopStream().then(() => {\n this.clear();\n return true;\n });\n }\n }\n\n _enableDebug() {\n this._log = console.log.bind(console);\n }\n\n _getHandle(handleId) {\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n return this._participants.find(p => p.handleId === handleId || rfid && p.rfid === rfid);\n }\n\n _getStats() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n return Promise.all(this._participants.map(participant => {\n let mediaTrack = null;\n\n if (type === 'video') {\n mediaTrack = participant.webrtcStuff && participant.webrtcStuff.stream && participant.webrtcStuff.stream.getVideoTracks().length && participant.webrtcStuff.stream.getVideoTracks()[0];\n } else if (type === 'audio') {\n mediaTrack = participant.webrtcStuff && participant.webrtcStuff.stream && participant.webrtcStuff.stream.getAudioTracks().length && participant.webrtcStuff.stream.getAudioTracks()[0];\n }\n\n return participant.webrtcStuff && participant.webrtcStuff.pc && participant.webrtcStuff.pc.getStats(mediaTrack).then(r => ({\n handle: participant,\n stats: r\n })).catch(e => Promise.resolve({\n handle: participant,\n stats: e\n }));\n }));\n }\n\n _sendTrickleCandidate(handleId, candidate) {\n return this._send({\n \"janus\": \"trickle\",\n \"candidate\": candidate,\n \"handle_id\": handleId\n });\n }\n\n _webrtc(handleId) {\n let enableOntrack = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'create rtc connection']\n });\n }\n\n let config = handle.webrtcStuff;\n\n if (!config.pc) {\n let pc_config = {\n \"iceServers\": this.iceServers,\n \"iceTransportPolicy\": 'all',\n \"bundlePolicy\": undefined\n };\n pc_config[\"sdpSemantics\"] = this.isUnifiedPlan ? \"unified-plan\" : \"plan-b\";\n let pc_constraints = {\n \"optional\": [{\n \"DtlsSrtpKeyAgreement\": true\n }]\n };\n\n if (this._ipv6Support === true) {\n pc_constraints.optional.push({\n \"googIPv6\": true\n });\n }\n\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"edge\") {\n // This is Edge, enable BUNDLE explicitly\n pc_config.bundlePolicy = \"max-bundle\";\n }\n\n this._log('new RTCPeerConnection', pc_config, pc_constraints);\n\n config.pc = new RTCPeerConnection(pc_config, pc_constraints);\n\n config.pc.onconnectionstatechange = () => {\n if (config.pc.connectionState === 'failed') {\n this._iceRestart(handleId);\n }\n\n this.emit('connectionState', [handleId, handleId === this.handleId, config.pc.connectionState]);\n\n if (handleId !== this.handleId && config.pc.connectionState === 'connected') {\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: handle.userId,\n stream: config.stream,\n track: null,\n optional: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n\n config.pc.oniceconnectionstatechange = () => {\n if (config.pc.iceConnectionState === 'failed') {\n this._iceRestart(handleId);\n }\n\n this.emit('iceState', [handleId, handleId === this.handleId, config.pc.iceConnectionState]);\n\n if (handleId !== this.handleId && (config.pc.iceConnectionState === 'completed' || config.pc.iceConnectionState === 'connected')) {\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: handle.userId,\n stream: config.stream,\n track: null,\n optional: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n\n config.pc.onicecandidate = event => {\n if (event.candidate == null || webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0) {\n config.iceDone = true;\n\n this._sendTrickleCandidate(handleId, {\n \"completed\": true\n }).catch(e => {\n this.emit('error', e);\n });\n } else {\n // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n var candidate = {\n \"candidate\": event.candidate.candidate,\n \"sdpMid\": event.candidate.sdpMid,\n \"sdpMLineIndex\": event.candidate.sdpMLineIndex\n };\n\n this._sendTrickleCandidate(handleId, candidate).catch(e => {\n this.emit('error', e);\n });\n }\n };\n\n if (enableOntrack) {\n config.pc.ontrack = event => {\n if (!event.streams) return; //config.stream = event.streams[0];\n\n if (!config.stream) {\n config.stream = new MediaStream();\n }\n\n if (event.track) {\n let mid = event.transceiver ? event.transceiver.mid : event.track.id;\n config.stream.addTrack(event.track);\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n mid,\n id: handle.handleId,\n userId: handle.userId,\n stream: config.stream,\n track: event.track,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n if (event.track.onended) return;\n\n event.track.onended = ev => {\n let mid = ev.target.id;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n mid = transceiver.mid;\n }\n\n if (config.stream) {\n config.stream && config.stream.removeTrack(ev.target);\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n mid,\n userId: handle.userId,\n stream: config.stream,\n track: ev.target,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: false,\n removing: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n\n event.track.onmute = ev => {\n this._log('remoteTrackMuted', 'onmute');\n\n let mid = ev.target.id;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n mid = transceiver.mid;\n }\n\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n userId: handle.userId,\n stream: config.stream,\n kind: ev.target.kind,\n track: ev.target,\n muted: true\n });\n };\n\n event.track.onunmute = ev => {\n this._log('remoteTrackMuted', 'onunmute');\n\n let mid = ev.target.id;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n mid = transceiver.mid;\n }\n\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n userId: handle.userId,\n stream: config.stream,\n kind: ev.target.kind,\n track: ev.target,\n muted: false\n });\n };\n }\n };\n }\n }\n\n if (!config.dataChannel || !config.dataChannelOpen) {\n config.dataChannel = {};\n\n var onDataChannelMessage = event => {\n this._handleDataEvents(handleId, 'message', event.data);\n };\n\n var onDataChannelStateChange = event => {\n let label = event.target.label;\n let protocol = event.target.protocol;\n let state = config.dataChannel[label] ? config.dataChannel[label].readyState : \"null\";\n\n this._handleDataEvents(handleId, 'state', {\n state,\n label\n });\n }; //TODO: check this\n\n\n var onDataChannelError = error => {\n var _error$channel;\n\n this._handleDataEvents(handleId, 'error', {\n label: error === null || error === void 0 ? void 0 : (_error$channel = error.channel) === null || _error$channel === void 0 ? void 0 : _error$channel.label,\n error\n });\n };\n\n const createDataChannel = (label, protocol, incoming) => {\n let options = {\n ordered: true\n };\n\n if (!incoming) {\n if (protocol) {\n options = { ...options,\n protocol\n };\n }\n\n config.dataChannel[label] = config.pc.createDataChannel(label, options);\n } else {\n config.dataChannel[label] = incoming;\n }\n\n config.dataChannel[label].onmessage = onDataChannelMessage;\n config.dataChannel[label].onopen = onDataChannelStateChange;\n config.dataChannel[label].onclose = onDataChannelStateChange;\n config.dataChannel[label].onerror = onDataChannelError;\n };\n\n createDataChannel(this.defaultDataChannelLabel, null, null);\n\n config.pc.ondatachannel = function (event) {\n createDataChannel(event.channel.label, event.channel.protocol, event.channel);\n };\n }\n }\n\n _webrtcPeer(handleId, jsep) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'rtc peer']\n });\n }\n\n var config = handle.webrtcStuff;\n\n if (jsep !== undefined && jsep !== null) {\n if (config.pc === null) {\n this._log(\"No PeerConnection: if this is an answer, use createAnswer and not _webrtcPeer\");\n\n return Promise.resolve(null);\n }\n\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp; // Any trickle candidate we cached?\n\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n }\n\n config.candidates = [];\n } // Done\n\n\n return true;\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 22,\n message: 'rtc peer',\n data: [handleId, 'invalid jsep']\n });\n }\n }\n\n _iceRestart(handleId) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return;\n }\n\n var config = handle.webrtcStuff; // Already restarting;\n\n if (config.isIceRestarting) {\n return;\n }\n\n if (this.handleId === handleId) {\n this._log('Performing local ICE restart');\n\n config.isIceRestarting = true;\n let hasAudio = !!(config.stream && config.stream.getAudioTracks().length > 0);\n let hasVideo = !!(config.stream && config.stream.getVideoTracks().length > 0);\n\n this._createAO('offer', handleId, true, [hasAudio, false, hasVideo, false]).then(jsep => {\n if (!jsep) {\n return null;\n }\n\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"audio\": hasAudio,\n \"video\": hasVideo,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {})\n },\n jsep\n });\n }).then(r => {\n config.isIceRestarting = false;\n\n this._log('ICE restart success');\n }).catch(e => {\n config.isIceRestarting = false;\n this.emit('warning', {\n type: 'error',\n id: 28,\n message: 'iceRestart failed',\n data: e\n });\n });\n } else {\n this._log('Performing remote ICE restart', handleId);\n\n config.isIceRestarting = true;\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"restart\": true\n }\n }).then(() => {\n config.isIceRestarting = false;\n }).catch(() => {\n config.isIceRestarting = false;\n });\n }\n }\n\n _createAO() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'offer';\n let handleId = arguments.length > 1 ? arguments[1] : undefined;\n let iceRestart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let [audioSend, audioRecv, videoSend, videoRecv] = arguments.length > 3 ? arguments[3] : undefined;\n\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'createAO', type]\n });\n }\n\n let methodName = null;\n\n if (type === 'offer') {\n methodName = 'createOffer';\n } else {\n methodName = 'createAnswer';\n }\n\n var config = handle.webrtcStuff; // https://code.google.com/p/webrtc/issues/detail?id=3508\n\n var mediaConstraints = {};\n\n if (this.isUnifiedPlan) {\n var audioTransceiver = null,\n videoTransceiver = null;\n var transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (var i in transceivers) {\n var t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\" && t.stopped === false) {\n if (!audioTransceiver) audioTransceiver = t;\n continue;\n }\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\" && t.stopped === false) {\n if (!videoTransceiver) videoTransceiver = t;\n continue;\n }\n }\n } // Handle audio (and related changes, if any)\n\n\n if (!audioSend && !audioRecv) {\n // Audio disabled: have we removed it?\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"inactive\");\n } else {\n audioTransceiver.direction = \"inactive\";\n }\n }\n } else {\n // Take care of audio m-line\n if (audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendrecv\");\n } else {\n audioTransceiver.direction = \"sendrecv\";\n }\n }\n } else if (audioSend && !audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendonly\");\n } else {\n audioTransceiver.direction = \"sendonly\";\n }\n }\n } else if (!audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"recvonly\");\n } else {\n audioTransceiver.direction = \"recvonly\";\n }\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n audioTransceiver = config.pc.addTransceiver(\"audio\", {\n direction: \"recvonly\"\n });\n }\n }\n } // Handle video (and related changes, if any)\n\n\n if (!videoSend && !videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"inactive\");\n } else {\n videoTransceiver.direction = \"inactive\";\n }\n }\n } else {\n // Take care of video m-line\n if (videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendrecv\");\n } else {\n videoTransceiver.direction = \"sendrecv\";\n }\n }\n } else if (videoSend && !videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendonly\");\n } else {\n videoTransceiver.direction = \"sendonly\";\n }\n }\n } else if (!videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"recvonly\");\n } else {\n videoTransceiver.direction = \"recvonly\";\n }\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n videoTransceiver = config.pc.addTransceiver(\"video\", {\n direction: \"recvonly\"\n });\n }\n }\n }\n } else {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"firefox\" || webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"edge\") {\n mediaConstraints = {\n offerToReceiveAudio: audioRecv,\n offerToReceiveVideo: videoRecv\n };\n } else {\n mediaConstraints = {\n mandatory: {\n OfferToReceiveAudio: audioRecv,\n OfferToReceiveVideo: videoRecv\n }\n };\n }\n }\n\n if (iceRestart) {\n mediaConstraints[\"iceRestart\"] = true;\n }\n\n return config.pc[methodName](mediaConstraints).then(function (response) {\n config.mySdp = response.sdp;\n\n let _p = config.pc.setLocalDescription(response).catch(e => {\n return Promise.reject({\n type: 'warning',\n id: 24,\n message: 'setLocalDescription',\n data: [handleId, e]\n });\n });\n\n config.mediaConstraints = mediaConstraints;\n\n if (!config.iceDone && !config.trickle) {\n // Don't do anything until we have all candidates\n return Promise.resolve(null);\n } // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n\n\n var jsep = {\n \"type\": response.type,\n \"sdp\": response.sdp\n };\n return _p.then(() => jsep);\n }, e => {\n return Promise.reject({\n type: 'warning',\n id: 25,\n message: methodName,\n data: [handleId, e]\n });\n });\n }\n\n _publishRemote(handleId, jsep) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'publish remote participant']\n });\n }\n\n this._webrtc(handleId, true);\n\n let config = handle.webrtcStuff;\n\n if (jsep) {\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp; // Any trickle candidate we cached?\n\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n\n if (!candidate || candidate.completed === true) {\n // end-of-candidates\n config.pc.addIceCandidate(null);\n } else {\n // New candidate\n config.pc.addIceCandidate(candidate);\n }\n }\n\n config.candidates = [];\n } // Create the answer now\n\n\n return this._createAO('answer', handleId, false, [false, true, false, true]).then(_jsep => {\n if (!_jsep) {\n this.emit('error', {\n type: 'warning',\n id: 19,\n message: 'publish remote participant',\n data: [handleId, 'no jsep']\n });\n return Promise.resolve();\n }\n\n return this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"start\",\n ...(this.roomId && {\n \"room\": this.roomId\n }),\n ...(this.pin && {\n pin: this.pin\n })\n },\n \"jsep\": _jsep\n });\n });\n }, e => Promise.reject({\n type: 'warning',\n id: 23,\n message: 'setRemoteDescription',\n data: [handleId, e]\n }));\n } else {\n return Promise.resolve();\n }\n } //Public methods\n\n\n publishLocal(stream) {\n let {\n keepAudio = false,\n keepVideo = false\n } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n this.emit('publishing', true);\n\n let handle = this._getHandle(this.handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 21,\n message: 'no local id, connect before publishing',\n data: null\n });\n }\n\n this._webrtc(this.handleId);\n\n let config = handle.webrtcStuff;\n\n if (stream) {\n if (!config.stream) {\n config.stream = stream;\n stream.getTracks().forEach(function (track) {\n config.pc.addTrack(track, stream);\n });\n } else {\n /* UPDATE Audio */\n let replaceAudio = stream.getAudioTracks().length;\n\n if (replaceAudio || !keepAudio) {\n //this will stop existing tracks\n let oldAudioStream = config.stream.getAudioTracks()[0];\n\n if (oldAudioStream) {\n config.stream.removeTrack(oldAudioStream);\n\n try {\n oldAudioStream.stop();\n } catch (e) {\n this._log(e);\n }\n }\n }\n\n if (config.pc.getSenders() && config.pc.getSenders().length) {\n if (replaceAudio && this.isUnifiedPlan) {//using replace\n } else {\n for (let index in config.pc.getSenders()) {\n let s = config.pc.getSenders()[index];\n\n if (s && s.track && s.track.kind === \"audio\") {\n config.pc.removeTrack(s);\n }\n }\n }\n }\n\n if (replaceAudio) {\n config.stream.addTrack(stream.getAudioTracks()[0]);\n var audioTransceiver = null;\n\n if (this.isUnifiedPlan) {\n let transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (let i in transceivers) {\n let t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\" && t.stopped === false) {\n audioTransceiver = t;\n break;\n }\n }\n }\n }\n\n if (audioTransceiver && audioTransceiver.sender) {\n audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);\n } else {\n config.pc.addTrack(stream.getAudioTracks()[0], stream);\n }\n }\n /* UPDATE Video */\n\n\n let replaceVideo = stream.getVideoTracks().length;\n\n if (replaceVideo || !keepVideo) {\n let oldVideoStream = config.stream.getVideoTracks()[0];\n\n if (oldVideoStream) {\n config.stream.removeTrack(oldVideoStream);\n\n try {\n oldVideoStream.stop();\n } catch (e) {\n this._log(e);\n }\n }\n }\n\n if (config.pc.getSenders() && config.pc.getSenders().length) {\n if (replaceVideo && this.isUnifiedPlan) {//using replace\n } else {\n for (let index in config.pc.getSenders()) {\n let s = config.pc.getSenders()[index];\n\n if (s && s.track && s.track.kind === \"video\") {\n config.pc.removeTrack(s);\n }\n }\n }\n }\n\n if (replaceVideo) {\n config.stream.addTrack(stream.getVideoTracks()[0]);\n var videoTransceiver = null;\n\n if (this.isUnifiedPlan) {\n let transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (let i in transceivers) {\n let t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\" && t.stopped === false) {\n videoTransceiver = t;\n break;\n }\n }\n }\n }\n\n if (videoTransceiver && videoTransceiver.sender) {\n //TODO: check if t.stopped === false still gets us videoTransceiver\n videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);\n } else {\n config.pc.addTrack(stream.getVideoTracks()[0], stream);\n }\n }\n }\n }\n\n let hasAudio = !!(stream && stream.getAudioTracks().length > 0);\n let hasVideo = !!(stream && stream.getVideoTracks().length > 0);\n let isAudioMuted = !stream || stream.getAudioTracks().length === 0 || !stream.getAudioTracks()[0].enabled;\n let isVideoMuted = !stream || stream.getVideoTracks().length === 0 || !stream.getVideoTracks()[0].enabled;\n this.isAudioEnabed = hasAudio;\n this.isVideoEnabled = hasVideo;\n this.isAudioMuted = isAudioMuted;\n this.isVideoMuted = isVideoMuted;\n return this._createAO('offer', this.handleId, false, [hasAudio, false, hasVideo, false]).then(jsep => {\n if (!jsep) {\n return null;\n } //HOTFIX: Temporary fix for Safari 13\n\n\n if (jsep.sdp && jsep.sdp.indexOf(\"\\r\\na=ice-ufrag\") === -1) {\n jsep.sdp = jsep.sdp.replace(\"\\na=ice-ufrag\", \"\\r\\na=ice-ufrag\");\n }\n\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"configure\",\n \"audio\": hasAudio,\n \"video\": hasVideo,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {})\n },\n jsep\n });\n }).then(r => {\n if (this._isDataChannelOpen) {\n return Promise.resolve(r);\n } else {\n return new Promise((resolve, reject) => {\n let __ = val => {\n if (val) {\n clearTimeout(this._dataChannelTimeoutId);\n this.off('dataChannel', __, this);\n resolve(this);\n }\n };\n\n this._dataChannelTimeoutId = setTimeout(() => {\n this.off('dataChannel', __, this);\n reject({\n type: 'error',\n id: 27,\n message: 'Data channel did not open',\n data: null\n });\n }, 5000);\n this.on('dataChannel', __, this);\n });\n }\n }).then(r => {\n this._isPublished = true;\n\n if (config.stream) {\n let tracks = config.stream.getTracks();\n tracks.forEach(track => {\n // used as a flag to not emit tracks that been already emitted\n if (!track.onended) {\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: handle.userId,\n track,\n stream: config.stream,\n adding: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n\n track.onended = ev => {\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: handle.userId,\n track: ev.target,\n stream: config.stream,\n adding: false,\n removing: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n };\n }\n });\n } else {\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: handle.userId,\n stream: null,\n adding: false,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n }\n\n this.emit('published', {\n status: true,\n hasStream: !!config.stream\n });\n this.emit('publishing', false);\n this.emit('localHasVideo', hasVideo);\n this.emit('localHasAudio', hasAudio);\n this.emit('localMuted', {\n type: 'video',\n value: isVideoMuted\n });\n this.emit('localMuted', {\n type: 'audio',\n value: isAudioMuted\n });\n return r;\n }).catch(e => {\n this.emit('publishing', false);\n return Promise.reject(e);\n });\n }\n\n unpublishLocal() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this._isPublished ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"unpublish\"\n }\n }, dontWait).finally(r => {\n this._isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n return r;\n }) : Promise.resolve();\n }\n\n toggleAudio() {\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let mid = arguments.length > 1 ? arguments[1] : undefined;\n\n let handle = this._getHandle(this.handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n }\n\n let config = handle.webrtcStuff;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.sender && t.sender.track && t.receiver.track.kind === \"audio\" && t.stopped === false && (mid ? t.mid === mid : true));\n\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n\n this.isAudioMuted = !transceiver || !transceiver.sender.track.enabled;\n } else {\n if (config.stream && config.stream.getAudioTracks().length) {\n config.stream.getAudioTracks()[0].enabled = value !== null ? !!value : !config.stream.getAudioTracks()[0].enabled;\n }\n\n this.isAudioMuted = config.stream.getAudioTracks().length === 0 || !config.stream.getAudioTracks()[0].enabled;\n }\n\n this.emit('localMuted', {\n type: 'audio',\n value: this.isAudioMuted,\n mid\n });\n }\n\n toggleVideo() {\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let mid = arguments.length > 1 ? arguments[1] : undefined;\n\n let handle = this._getHandle(this.handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n }\n\n let config = handle.webrtcStuff;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.sender && t.sender.track && t.receiver.track.kind === \"video\" && t.stopped === false && (mid ? t.mid === mid : true));\n\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n\n this.isVideoMuted = !transceiver || !transceiver.sender.track.enabled;\n } else {\n if (config.stream && config.stream.getVideoTracks().length) {\n config.stream.getVideoTracks()[0].enabled = value !== null ? !!value : !config.stream.getVideoTracks()[0].enabled;\n }\n\n this.isVideoMuted = config.stream.getVideoTracks().length === 0 || !config.stream.getVideoTracks()[0].enabled;\n }\n\n this.emit('localMuted', {\n type: 'video',\n value: this.isVideoMuted,\n mid\n });\n }\n\n setInstructorId() {\n let instructorId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n this._instuctorId = instructorId;\n this.emit('instructorId', this._instuctorId);\n return this._instuctorId;\n }\n\n setObserverIds() {\n let observerIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n this._observerIds = observerIds;\n this.emit('observerIds', this._observerIds);\n return this._observerIds;\n }\n\n setTalkbackIds() {\n let talkbackIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n this._talkbackIds = talkbackIds;\n this.emit('talkbackIds', this._talkbackIds);\n return this._talkbackIds;\n }\n\n}\n\n_defineProperty(RoomSession, \"sessionTypes\", {\n 'reactooroom': 'janus.plugin.reactooroom',\n 'streaming': 'janus.plugin.streaming'\n});\n\n_defineProperty(RoomSession, \"subscriptionRules\", {\n participant: {\n watchTogether: ['participant', 'talkback'],\n videoWall: ['instructor', 'observer', 'talkback']\n },\n monitor: {\n watchTogether: ['participant'],\n videoWall: ['instructor', 'participant']\n },\n talkback: {\n watchTogether: ['participant'],\n videoWall: ['instructor', 'participant']\n },\n observer: {\n watchTogether: ['participant'],\n videoWall: ['participant']\n },\n instructor: {\n watchTogether: [],\n videoWall: []\n }\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Room);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/wt-room.js?");
|
|
9395
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webrtc-adapter */ \"./node_modules/webrtc-adapter/src/js/adapter_core.js\");\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./wt-emitter */ \"./src/modules/wt-emitter.js\");\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./wt-utils */ \"./src/modules/wt-utils.js\");\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n// Watch together janus webrtc library\n\n\n\n\nclass Room {\n constructor(debug) {\n this.debug = debug;\n this.sessions = [];\n this.safariVp8 = false;\n this.browser = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser;\n this.browserDetails = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails;\n this.webrtcSupported = Room.isWebrtcSupported();\n this.safariVp8TestPromise = Room.testSafariVp8();\n this.safariVp8 = null;\n this.safariVp8TestPromise.then(safariVp8 => {\n this.safariVp8 = safariVp8;\n }); // Let's get it started\n\n this.whenInitialized = this.initialize();\n }\n\n initialize() {\n return this.safariVp8TestPromise.then(() => this);\n }\n\n createSession() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return new RoomSession(constructId, type, {\n debug: this.debug,\n ...options\n });\n }\n\n static testSafariVp8() {\n return new Promise(resolve => {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'safari' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 605) {\n if (RTCRtpSender && RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities(\"video\") && RTCRtpSender.getCapabilities(\"video\").codecs && RTCRtpSender.getCapabilities(\"video\").codecs.length) {\n var isVp8 = false;\n\n for (var i in RTCRtpSender.getCapabilities(\"video\").codecs) {\n var codec = RTCRtpSender.getCapabilities(\"video\").codecs[i];\n\n if (codec && codec.mimeType && codec.mimeType.toLowerCase() === \"video/vp8\") {\n isVp8 = true;\n break;\n }\n }\n\n resolve(isVp8);\n } else {\n // We do it in a very ugly way, as there's no alternative...\n // We create a PeerConnection to see if VP8 is in an offer\n var testpc = new RTCPeerConnection({}, {});\n testpc.createOffer({\n offerToReceiveVideo: true\n }).then(function (offer) {\n let result = offer.sdp.indexOf(\"VP8\") !== -1;\n testpc.close();\n testpc = null;\n resolve(result);\n });\n }\n } else resolve(false);\n });\n }\n\n static isWebrtcSupported() {\n return window.RTCPeerConnection !== undefined && window.RTCPeerConnection !== null && navigator.mediaDevices !== undefined && navigator.mediaDevices !== null && navigator.mediaDevices.getUserMedia !== undefined && navigator.mediaDevices.getUserMedia !== null;\n }\n\n}\n\nclass RoomSession {\n static noop() {}\n\n static randomString(len) {\n var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n var randomString = '';\n\n for (var i = 0; i < len; i++) {\n var randomPoz = Math.floor(Math.random() * charSet.length);\n randomString += charSet.substring(randomPoz, randomPoz + 1);\n }\n\n return randomString;\n } //TODO: solve\n // #eventList = ['error', 'kicked', 'addLocalParticipant', ,'addRemoteInstructor','addRemoteParticipant','addRemoteTalkback', 'addRemoteObserver', 'removeRemoteInstructor', 'removeLocalParticipant', 'removeRemoteParticipant', 'removeRemoteTalkback', 'removeRemoteObserver', 'localMuted', 'localHasVideo', 'localHasAudio', 'data', 'iceState', 'connectionState', 'joined', 'joining', 'dataChannel', 'disconnect', 'observerIds', 'talkbackIds', 'instructorId', 'published', 'publishing', 'remoteTrackMuted', 'streamingStatus', 'streaming', 'streamStarting'];\n //\n\n\n constructor() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n Object.assign(this, Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])());\n this.defaultDataChannelLabel = 'JanusDataChannel';\n this.server = null;\n this.iceServers = null;\n this.token = null;\n this.roomId = null;\n this.streamId = null;\n this.pin = null;\n this.userId = null;\n this.sessiontype = type;\n this.initialBitrate = 0;\n this.recordingFilename = null;\n this.pluginName = RoomSession.sessionTypes[type];\n this.options = options;\n this.id = null;\n this.privateId = null;\n this.constructId = constructId || RoomSession.randomString(16);\n this.sessionId = null;\n this.handleId = null;\n this.ws = null;\n this.isRestarting = false;\n this.isConnecting = false;\n this.isDisconnecting = false;\n this.isConnected = false;\n this.isPublished = false;\n this.isReclaiming = false;\n this.isStreaming = false;\n this._ipv6Support = false;\n this._retries = 0;\n this._maxRetries = 3;\n this._keepAliveId = null;\n this._participants = [];\n this._observerIds = [];\n this._talkbackIds = [];\n this._instuctorId = null;\n this._roomType = 'watchparty';\n this._isDataChannelOpen = false;\n this._abortController = null;\n this.isAudioMuted = false;\n this.isVideoMuted = false;\n this.isVideoEnabled = false;\n this.isAudioEnabed = false;\n this.isUnifiedPlan = RoomSession.checkUnifiedPlan();\n this.subscriptionRules = { ...RoomSession.subscriptionRules,\n ...(this.options.subscriptionRules || {})\n };\n this._log = RoomSession.noop;\n\n if (this.options.debug) {\n this._enableDebug();\n }\n } // Check if this browser supports Unified Plan and transceivers\n // Based on https://codepen.io/anon/pen/ZqLwWV?editors=0010\n\n\n static checkUnifiedPlan() {\n let unifiedPlan = false;\n\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'firefox' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 59) {\n // Firefox definitely does, starting from version 59\n unifiedPlan = true;\n } else if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'chrome' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 72) {\n // Chrome does, but it's only usable from version 72 on\n unifiedPlan = true;\n } else if (!window.RTCRtpTransceiver || !('currentDirection' in RTCRtpTransceiver.prototype)) {\n // Safari supports addTransceiver() but not Unified Plan when\n // currentDirection is not defined (see codepen above).\n unifiedPlan = false;\n } else {\n // Check if addTransceiver() throws an exception\n const tempPc = new RTCPeerConnection();\n\n try {\n tempPc.addTransceiver('audio');\n unifiedPlan = true;\n } catch (e) {}\n\n tempPc.close();\n }\n\n return unifiedPlan;\n }\n\n _participantShouldSubscribe(userId) {\n var _decodeJanusDisplay, _decodeJanusDisplay2;\n\n const myUserId = (_decodeJanusDisplay = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.userId)) === null || _decodeJanusDisplay === void 0 ? void 0 : _decodeJanusDisplay.userId;\n const remoteUserId = (_decodeJanusDisplay2 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(userId)) === null || _decodeJanusDisplay2 === void 0 ? void 0 : _decodeJanusDisplay2.userId;\n let allowedObservers = this._observerIds || [];\n let allowedTalkback = this._talkbackIds || [];\n let allowedInstructor = this._instuctorId || null;\n let localUserRole = 'participant';\n\n if (allowedObservers.indexOf(myUserId) > -1) {\n localUserRole = 'observer';\n } else if (allowedTalkback.indexOf(myUserId) > -1) {\n localUserRole = 'talkback';\n } else if (myUserId === allowedInstructor) {\n localUserRole = 'instructor';\n }\n\n let remoteUserRole = 'participant';\n\n if (allowedObservers.indexOf(remoteUserId) > -1) {\n remoteUserRole = 'observer';\n } else if (allowedTalkback.indexOf(remoteUserId) > -1) {\n remoteUserRole = 'talkback';\n } else if (remoteUserId === allowedInstructor) {\n remoteUserRole = 'instructor';\n }\n\n return this.subscriptionRules[localUserRole][this._roomType || 'watchparty'].indexOf(remoteUserRole) > -1;\n }\n\n _getAddParticipantEventName(handleId) {\n var _decodeJanusDisplay3;\n\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n }\n\n const decodedUserId = (_decodeJanusDisplay3 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay3 === void 0 ? void 0 : _decodeJanusDisplay3.userId;\n let allowedTalkback = this._talkbackIds || [];\n let allowedObservers = this._observerIds || [];\n let allowedInstructor = this._instuctorId || null;\n let eventName = 'addRemoteParticipant';\n\n if (decodedUserId === allowedInstructor) {\n eventName = 'addRemoteInstructor';\n }\n\n if (allowedTalkback.indexOf(decodedUserId) > -1) {\n eventName = 'addRemoteTalkback';\n }\n\n if (allowedObservers.indexOf(decodedUserId) > -1) {\n eventName = 'addRemoteObserver';\n }\n\n return eventName;\n }\n\n _getRemoveParticipantEventName(handleId) {\n var _decodeJanusDisplay4;\n\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n }\n\n const decodedUserId = (_decodeJanusDisplay4 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay4 === void 0 ? void 0 : _decodeJanusDisplay4.userId;\n let allowedTalkback = this._talkbackIds || [];\n let allowedObservers = this._observerIds || [];\n let allowedInstructor = this._instuctorId || null;\n let eventName = 'removeRemoteParticipant';\n\n if (decodedUserId === allowedInstructor) {\n eventName = 'removeRemoteInstructor';\n }\n\n if (allowedTalkback.indexOf(decodedUserId) > -1) {\n eventName = 'removeRemoteTalkback';\n }\n\n if (allowedObservers.indexOf(decodedUserId) > -1) {\n eventName = 'removeRemoteObserver';\n }\n\n return eventName;\n }\n\n sendMessage(handleId) {\n let message = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n body: 'Example Body'\n };\n let dontWait = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let dontResolveOnAck = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n return this._send({\n \"janus\": \"message\",\n \"handle_id\": handleId,\n ...message\n }, dontWait, dontResolveOnAck).then(json => {\n if (json && json[\"janus\"] === \"success\") {\n let plugindata = json[\"plugindata\"] || {};\n let data = plugindata[\"data\"];\n return Promise.resolve(data);\n }\n\n return Promise.resolve();\n }).catch(json => {\n if (json && json[\"error\"]) {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json[\"error\"]\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json\n });\n }\n });\n }\n\n _send() {\n let request = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let ignoreResponse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n let dontResolveOnAck = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let transaction = RoomSession.randomString(12);\n let requestData = { ...request,\n transaction,\n token: this.token,\n ...(this.sessionId && {\n 'session_id': this.sessionId\n } || {})\n };\n\n this._log(requestData);\n\n return new Promise((resolve, reject) => {\n let messageTimeoutId = null;\n\n let abortResponse = () => {\n this._abortController.signal.removeEventListener('abort', abortResponse);\n\n clearTimeout(messageTimeoutId);\n this.ws.removeEventListener('message', parseResponse);\n reject({\n type: 'warning',\n id: 17,\n message: 'connection cancelled'\n });\n };\n\n let parseResponse = event => {\n let json = JSON.parse(event.data);\n let r_transaction = json['transaction'];\n\n if (r_transaction === transaction && (!dontResolveOnAck || json['janus'] !== 'ack')) {\n clearTimeout(messageTimeoutId);\n\n this._abortController.signal.removeEventListener('abort', abortResponse);\n\n this.ws.removeEventListener('message', parseResponse);\n\n if (json['janus'] === 'error') {\n var _json$error;\n\n if ((json === null || json === void 0 ? void 0 : (_json$error = json.error) === null || _json$error === void 0 ? void 0 : _json$error.code) == 403) {\n this.disconnect(true);\n }\n\n reject({\n type: 'error',\n id: 2,\n message: 'send failed',\n data: json,\n requestData\n });\n } else {\n resolve(json);\n }\n }\n };\n\n if (ignoreResponse) {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.send(JSON.stringify(requestData));\n }\n\n resolve();\n } else {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.addEventListener('message', parseResponse);\n messageTimeoutId = setTimeout(() => {\n this.ws.removeEventListener('message', parseResponse);\n\n this._abortController.signal.removeEventListener('abort', abortResponse);\n\n reject({\n type: 'error',\n id: 3,\n message: 'send timeout',\n data: requestData\n });\n }, 10000);\n\n this._abortController.signal.addEventListener('abort', abortResponse);\n\n this.ws.send(JSON.stringify(requestData));\n } else {\n reject({\n type: 'warning',\n id: 29,\n message: 'No connection to WebSockets',\n data: requestData\n });\n }\n }\n });\n }\n\n _connectionClosed() {\n if (!this.isConnected || this.isConnecting || this.isDisconnecting) {\n return;\n }\n\n if (this._retries < this._maxRetries) {\n setTimeout(() => {\n this._retries++;\n\n this._reconnect().catch(e => {\n this.emit('error', e);\n });\n }, 3000 * this._retries);\n } else {\n if (this.sessiontype === 'reactooroom') {\n this.disconnect(true);\n } else if (this.sessiontype === 'streaming') {\n this.stopStream();\n }\n\n this.emit('error', {\n type: 'error',\n id: 4,\n message: 'Lost connection to WebSockets',\n data: null\n });\n }\n }\n\n _wipeListeners() {\n if (this.ws) {\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n }\n }\n\n _startKeepAlive() {\n this._send({\n \"janus\": \"keepalive\"\n }).then(json => {\n if (json[\"janus\"] !== 'ack') {\n this.emit('error', {\n type: 'warning',\n id: 5,\n message: 'keepalive response suspicious',\n data: json[\"janus\"]\n });\n }\n }).catch(e => {\n this.emit('error', {\n type: 'warning',\n id: 6,\n message: 'keepalive dead',\n data: e\n });\n\n this._connectionClosed();\n });\n\n this._keepAliveId = setTimeout(() => {\n this._startKeepAlive();\n }, 30000);\n }\n\n _stopKeepAlive() {\n clearTimeout(this._keepAliveId);\n }\n\n _handleWsEvents(event) {\n let json = JSON.parse(event.data);\n var sender = json[\"sender\"];\n var type = json[\"janus\"];\n\n let handle = this._getHandle(sender);\n\n if (!handle) {\n return;\n }\n\n if (type === \"trickle\") {\n let candidate = json[\"candidate\"];\n let config = handle.webrtcStuff;\n\n if (config.pc && config.remoteSdp) {\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n } else {\n if (!config.candidates) {\n config.candidates = [];\n }\n\n config.candidates.push(candidate);\n }\n } else if (type === \"webrtcup\") {//none universal\n } else if (type === \"hangup\") {\n this._log('hangup on', handle.handleId);\n\n this._removeParticipant(handle.handleId, null, false);\n } else if (type === \"detached\") {\n this._log('detached on', handle.handleId);\n\n this._removeParticipant(handle.handleId, null, true);\n } else if (type === \"media\") {\n this._log('Media event:', handle.handleId, json[\"type\"], json[\"receiving\"], json[\"mid\"]);\n } else if (type === \"slowlink\") {\n this._log('Slowlink', handle.handleId, json[\"uplink\"], json[\"lost\"], json[\"mid\"]);\n } else if (type === \"event\") {//none universal\n } else if (type === 'timeout') {\n this.ws.close(3504, \"Gateway timeout\");\n } else if (type === 'success' || type === 'error') {// we're capturing those elsewhere\n } else {\n this._log(\"Unknown event: \".concat(type, \" on session: \").concat(this.sessionId));\n } // LOCAL\n\n\n if (sender === this.handleId) {\n if (type === \"event\") {\n var plugindata = json[\"plugindata\"] || {};\n var msg = plugindata[\"data\"] || {};\n var jsep = json[\"jsep\"];\n let result = msg[\"result\"] || null;\n let event = msg[\"videoroom\"] || null;\n let list = msg[\"publishers\"] || {};\n let leaving = msg[\"leaving\"]; //let joining = msg[\"joining\"];\n\n let unpublished = msg[\"unpublished\"];\n let error = msg[\"error\"];\n\n if (event === \"joined\") {\n this.id = msg[\"id\"];\n this.privateId = msg[\"private_id\"];\n this.isConnected = true;\n this.emit('joined', true);\n\n this._log('We have successfully joined Room');\n\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n\n this._log('Remote userId: ', userId);\n\n if (this._participantShouldSubscribe(userId)) {\n this._log('Creating user: ', userId);\n\n this._createParticipant(userId, id).then(handle => {\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"feed\": id,\n \"private_id\": this.privateId,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (event === \"event\") {\n if (msg[\"streams\"] !== undefined && msg[\"streams\"] !== null) {\n this._log('Got my own streams back', msg[\"streams\"]);\n }\n\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n\n this._log('Remote userId: ', userId);\n\n if (this._participantShouldSubscribe(userId)) {\n this._log('Creating user: ', userId);\n\n this._createParticipant(userId, id).then(handle => {\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"feed\": id,\n \"private_id\": this.privateId,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n\n if (leaving === 'ok') {\n this._log('leaving', this.handleId, 'this is us');\n\n this._removeParticipant(this.handleId);\n\n if (msg['reason'] === 'kicked') {\n this.emit('kicked');\n this.disconnect().catch(() => {});\n }\n } else if (leaving) {\n this._log('leaving', leaving);\n\n this._removeParticipant(null, leaving);\n }\n\n if (unpublished === 'ok') {\n this._log('unpublished', this.handleId, 'this is us');\n\n this._removeParticipant(this.handleId, null, false); // we do just hangup\n\n } else if (unpublished) {\n this._log('unpublished', unpublished);\n\n this._removeParticipant(null, unpublished, true); // we do hangup and detach\n\n }\n\n if (error) {\n this.emit('error', {\n type: 'error',\n id: 7,\n message: 'local participant error',\n data: [sender, msg]\n });\n }\n } // Streaming related\n else if (result && result[\"status\"]) {\n this.emit('streamingStatus', result[\"status\"]);\n\n if (result[\"status\"] === 'stopped') {\n this.stopStream();\n }\n\n if (result[\"status\"] === 'started') {\n this.emit('streaming', true);\n this.isStreaming = true;\n }\n }\n\n if (jsep !== undefined && jsep !== null) {\n if (this.sessiontype === 'reactooroom') {\n this._webrtcPeer(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n } else if (this.sessiontype === 'streaming') {\n this._publishRemote(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (type === \"webrtcup\") {\n this._log('Configuring bitrate: ' + this.initialBitrate);\n\n if (this.initialBitrate > 0) {\n this.sendMessage(this.handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"bitrate\": this.initialBitrate\n }\n }).catch(() => null);\n }\n }\n } //REMOTE\n else {\n let plugindata = json[\"plugindata\"] || {};\n let msg = plugindata[\"data\"] || {};\n let jsep = json[\"jsep\"];\n let event = msg[\"videoroom\"];\n let error = msg[\"error\"];\n\n if (event === \"attached\") {\n var _decodeJanusDisplay5;\n\n this._log('Remote have successfully joined Room', msg);\n\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay5 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay5 === void 0 ? void 0 : _decodeJanusDisplay5.userId,\n stream: null,\n track: null,\n adding: false,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: false,\n hasVideoTrack: false\n });\n }\n\n if (error) {\n this.emit('error', {\n type: 'warning',\n id: 8,\n message: 'remote participant error',\n data: [sender, msg]\n });\n }\n\n if (jsep) {\n this._publishRemote(handle.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n }\n\n _handleDataEvents(handleId, type, data) {\n let handle = this._getHandle(handleId);\n\n if (type === 'state') {\n this._log(\" - Data channel status - \", \"UID: \".concat(handleId), \"STATUS: \".concat(JSON.stringify(data)), \"ME: \".concat(handleId === this.handleId));\n\n if (handle) {\n let config = handle.webrtcStuff;\n config.dataChannelOpen = this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label) && (data === null || data === void 0 ? void 0 : data.state) === 'open';\n }\n\n if (handleId === this.handleId && this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label)) {\n this._isDataChannelOpen = (data === null || data === void 0 ? void 0 : data.state) === 'open';\n this.emit('dataChannel', (data === null || data === void 0 ? void 0 : data.state) === 'open');\n }\n }\n\n if (type === 'error') {\n this.emit('error', {\n type: 'warning',\n id: 9,\n message: 'data event warning',\n data: [handleId, data]\n });\n\n if (handle) {\n let config = handle.webrtcStuff;\n\n if (this.defaultDataChannelLabel === data.label) {\n config.dataChannelOpen = false;\n }\n }\n\n if (handleId === this.handleId && this.defaultDataChannelLabel === data.label) {\n this._isDataChannelOpen = false;\n this.emit('dataChannel', false);\n }\n }\n\n if (handleId === this.handleId && type === 'message') {\n let d = null;\n\n try {\n d = JSON.parse(data);\n } catch (e) {\n this.emit('error', {\n type: 'warning',\n id: 10,\n message: 'data message parse error',\n data: [handleId, e]\n });\n return;\n }\n\n this.emit('data', d);\n }\n } //removeHandle === true -> hangup, detach, removeHandle === false -> hangup\n\n\n _removeParticipant(handleId, rfid) {\n let removeHandle = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n\n let handle = this._getHandle(handleId, rfid);\n\n if (!handle) {\n return Promise.resolve();\n } else {\n handleId = handle.handleId;\n }\n\n return this._send({\n \"janus\": \"hangup\",\n \"handle_id\": handleId\n }, true).then(() => removeHandle ? this._send({\n \"janus\": \"detach\",\n \"handle_id\": handleId\n }, true) : Promise.resolve()).finally(() => {\n try {\n if (handle.webrtcStuff.stream && !this.isRestarting) {\n handle.webrtcStuff.stream.getTracks().forEach(track => track.stop());\n }\n } catch (e) {// Do nothing\n }\n\n handle.webrtcStuff.stream = null;\n\n if (handle.webrtcStuff.dataChannel) {\n Object.keys(handle.webrtcStuff.dataChannel).forEach(label => {\n handle.webrtcStuff.dataChannel[label].onmessage = null;\n handle.webrtcStuff.dataChannel[label].onopen = null;\n handle.webrtcStuff.dataChannel[label].onclose = null;\n handle.webrtcStuff.dataChannel[label].onerror = null;\n });\n }\n\n if (handle.webrtcStuff.pc) {\n handle.webrtcStuff.pc.onicecandidate = null;\n handle.webrtcStuff.pc.ontrack = null;\n handle.webrtcStuff.pc.ondatachannel = null;\n handle.webrtcStuff.pc.onconnectionstatechange = null;\n handle.webrtcStuff.pc.oniceconnectionstatechange = null;\n }\n\n try {\n handle.webrtcStuff.pc.close();\n } catch (e) {}\n\n handle.webrtcStuff = {\n stream: null,\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false\n };\n\n if (handleId === this.handleId) {\n var _decodeJanusDisplay6;\n\n this._isDataChannelOpen = false;\n this.isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('removeLocalParticipant', {\n id: handleId,\n userId: (_decodeJanusDisplay6 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay6 === void 0 ? void 0 : _decodeJanusDisplay6.userId\n });\n } else {\n var _decodeJanusDisplay7;\n\n this.emit(this._getRemoveParticipantEventName(handleId), {\n id: handleId,\n userId: (_decodeJanusDisplay7 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay7 === void 0 ? void 0 : _decodeJanusDisplay7.userId\n });\n }\n\n if (removeHandle) {\n let handleIndex = this._participants.findIndex(p => p.handleId === handleId);\n\n this._participants.splice(handleIndex, 1);\n }\n\n return true;\n });\n }\n\n _createParticipant() {\n let userId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n return this._send({\n \"janus\": \"attach\",\n \"plugin\": this.pluginName\n }).then(json => {\n let handleId = json.data[\"id\"];\n let handle = {\n handleId,\n rfid,\n userId,\n webrtcStuff: {\n stream: null,\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n isIceRestarting: false\n }\n };\n\n this._participants.push(handle);\n\n return handle;\n });\n }\n\n _joinRoom(roomId, pin, userId, display) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": roomId,\n \"pin\": pin,\n \"ptype\": \"publisher\",\n \"display\": display,\n ...(this.webrtcVersion > 1000 ? {\n id: userId\n } : {})\n }\n }, false, true);\n }\n\n _watchStream(id) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"watch\",\n id: parseInt(id)\n }\n }, false, true);\n }\n\n _leaveRoom() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this.isConnected ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"leave\"\n }\n }, dontWait).finally(() => {\n this.isConnected = false;\n this.emit('joined', false);\n }) : Promise.resolve();\n } // internal reconnect\n\n\n _reconnect() {\n if (this.isReclaiming) {\n return Promise.resolve();\n }\n\n if (this.ws) {\n this._wipeListeners();\n\n if (this.ws.readyState === 1) {\n this.ws.close();\n }\n }\n\n this._stopKeepAlive();\n\n this.isReclaiming = true;\n this.emit('joining', true);\n return new Promise((resolve, reject) => {\n let abortReconnect = () => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled',\n data: e\n });\n };\n\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n\n this._send({\n \"janus\": \"claim\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n this._startKeepAlive();\n\n this.isReclaiming = false;\n this.emit('joining', false);\n this._retries = 0;\n resolve(json);\n }).catch(error => {\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 11,\n message: 'reconnection error',\n data: error\n });\n });\n }; // this is called before 'close' event callback so it doesn't break reconnect loop\n\n\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 12,\n message: 'ws reconnection error',\n data: e\n });\n };\n\n this._abortController.signal.addEventListener('abort', abortReconnect);\n });\n }\n\n connect(roomId, pin, server, iceServers, token, display, userId) {\n let webrtcVersion = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 0;\n let initialBitrate = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : 0;\n let recordingFilename = arguments.length > 9 ? arguments[9] : undefined;\n\n if (this.isConnecting) {\n return Promise.reject({\n type: 'warning',\n id: 16,\n message: 'connection already in progress'\n });\n }\n\n if (this.ws) {\n this._wipeListeners();\n }\n\n this._stopKeepAlive();\n\n this._abortController = new AbortController();\n this.sessionId = null;\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.roomId = roomId;\n this.pin = pin;\n this.display = display;\n this.userId = userId;\n this.webrtcVersion = webrtcVersion;\n this.initialBitrate = initialBitrate;\n this.recordingFilename = recordingFilename;\n this.isConnecting = true;\n this.emit('joining', true);\n return new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n\n let abortConnect = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n\n this._retries = 0;\n\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n this._startKeepAlive();\n\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._joinRoom(roomId, pin, userId, display)).then(() => {\n this.isConnecting = false;\n this.emit('joining', false);\n resolve(this);\n }).catch(error => {\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: (error === null || error === void 0 ? void 0 : error.type) === 'warning' ? 'warning' : 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n\n this._abortController.signal.addEventListener('abort', abortConnect);\n });\n }\n\n disconnect() {\n var _this$_abortControlle, _this$_abortControlle2;\n\n if (this.isDisconnecting) {\n return Promise.resolve();\n }\n\n (_this$_abortControlle = this._abortController) === null || _this$_abortControlle === void 0 ? void 0 : (_this$_abortControlle2 = _this$_abortControlle.abort) === null || _this$_abortControlle2 === void 0 ? void 0 : _this$_abortControlle2.call(_this$_abortControlle);\n\n this._stopKeepAlive();\n\n let isConnected = this.isConnected;\n this.isDisconnecting = true;\n return Promise.all(this._participants.map(p => this._removeParticipant(p.handleId))).finally(() => {\n this._wipeListeners();\n\n if (this.ws && this.ws.readyState === 1) {\n this._send({\n \"janus\": \"destroy\"\n }, true);\n\n this.ws.close();\n }\n\n this.sessionId = null;\n this.isPublished = false;\n this.isConnected = false;\n this.isDisconnecting = false;\n this.emit('publishing', false);\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('joining', false);\n this.emit('joined', false);\n this.emit('disconnect', isConnected);\n return Promise.resolve('Disconnected');\n });\n }\n\n startStream(streamId, server, iceServers, token, userId) {\n if (this.isConnecting) {\n return Promise.reject({\n type: 'warning',\n id: 16,\n message: 'connection error',\n data: 'Connection is in progress'\n });\n }\n\n if (this.ws) {\n this._wipeListeners();\n }\n\n this._stopKeepAlive();\n\n this._abortController = new AbortController();\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.streamId = streamId;\n this.userId = userId;\n this.sessionId = null;\n this.isConnecting = true;\n this.emit('streamStarting', true);\n return new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n\n let abortConnect = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n\n this._retries = 0;\n\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n this._startKeepAlive();\n\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._watchStream(streamId)).then(() => {\n this.isConnecting = false;\n this.emit('streamStarting', false);\n resolve(this);\n }).catch(error => {\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n\n this._abortController.signal.addEventListener('abort', abortConnect);\n });\n }\n\n stopStream() {\n var _this$_abortControlle3, _this$_abortControlle4;\n\n if (this.isDisconnecting) {\n return Promise.resolve();\n }\n\n (_this$_abortControlle3 = this._abortController) === null || _this$_abortControlle3 === void 0 ? void 0 : (_this$_abortControlle4 = _this$_abortControlle3.abort) === null || _this$_abortControlle4 === void 0 ? void 0 : _this$_abortControlle4.call(_this$_abortControlle3);\n\n this._stopKeepAlive();\n\n let isStreaming = this.isStreaming;\n this.isDisconnecting = true;\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"stop\"\n }\n }, false, true).then(() => this._removeParticipant(this.handleId)).finally(() => {\n this._wipeListeners();\n\n this._send({\n \"janus\": \"destroy\"\n }, true);\n\n if (this.ws && this.ws.readyState === 1) {\n this.ws.close();\n }\n\n this.sessionId = null;\n this.isDisconnecting = false;\n this.isStreaming = false;\n this.emit('streamStarting', false);\n this.emit('streaming', false);\n this.emit('disconnect', isStreaming);\n return Promise.resolve('Disconnected');\n });\n }\n\n destroy() {\n if (this.sessiontype === 'reactooroom') {\n return this.disconnect().then(() => {\n this.clear();\n return true;\n });\n } else if (this.sessiontype === 'streaming') {\n return this.stopStream().then(() => {\n this.clear();\n return true;\n });\n }\n }\n\n _enableDebug() {\n this._log = console.log.bind(console);\n }\n\n _getHandle(handleId) {\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n return this._participants.find(p => p.handleId === handleId || rfid && p.rfid === rfid);\n }\n\n _getStats() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n return Promise.all(this._participants.map(participant => {\n let mediaTrack = null;\n\n if (type === 'video') {\n mediaTrack = participant.webrtcStuff && participant.webrtcStuff.stream && participant.webrtcStuff.stream.getVideoTracks().length && participant.webrtcStuff.stream.getVideoTracks()[0];\n } else if (type === 'audio') {\n mediaTrack = participant.webrtcStuff && participant.webrtcStuff.stream && participant.webrtcStuff.stream.getAudioTracks().length && participant.webrtcStuff.stream.getAudioTracks()[0];\n }\n\n return participant.webrtcStuff && participant.webrtcStuff.pc && participant.webrtcStuff.pc.getStats(mediaTrack).then(r => ({\n handle: participant,\n stats: r\n })).catch(e => Promise.resolve({\n handle: participant,\n stats: e\n }));\n }));\n }\n\n _sendTrickleCandidate(handleId, candidate) {\n return this._send({\n \"janus\": \"trickle\",\n \"candidate\": candidate,\n \"handle_id\": handleId\n });\n }\n\n _webrtc(handleId) {\n let enableOntrack = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'create rtc connection']\n });\n }\n\n let config = handle.webrtcStuff;\n\n if (!config.pc) {\n let pc_config = {\n \"iceServers\": this.iceServers,\n \"iceTransportPolicy\": 'all',\n \"bundlePolicy\": undefined\n };\n pc_config[\"sdpSemantics\"] = this.isUnifiedPlan ? \"unified-plan\" : \"plan-b\";\n let pc_constraints = {\n \"optional\": [{\n \"DtlsSrtpKeyAgreement\": true\n }]\n };\n\n if (this._ipv6Support === true) {\n pc_constraints.optional.push({\n \"googIPv6\": true\n });\n }\n\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"edge\") {\n // This is Edge, enable BUNDLE explicitly\n pc_config.bundlePolicy = \"max-bundle\";\n }\n\n this._log('new RTCPeerConnection', pc_config, pc_constraints);\n\n config.pc = new RTCPeerConnection(pc_config, pc_constraints);\n\n config.pc.onconnectionstatechange = () => {\n if (config.pc.connectionState === 'failed') {\n this._iceRestart(handleId);\n }\n\n this.emit('connectionState', [handleId, handleId === this.handleId, config.pc.connectionState]);\n\n if (handleId !== this.handleId && config.pc.connectionState === 'connected') {\n var _decodeJanusDisplay8;\n\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay8 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay8 === void 0 ? void 0 : _decodeJanusDisplay8.userId,\n stream: config.stream,\n track: null,\n optional: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n\n config.pc.oniceconnectionstatechange = () => {\n if (config.pc.iceConnectionState === 'failed') {\n this._iceRestart(handleId);\n }\n\n this.emit('iceState', [handleId, handleId === this.handleId, config.pc.iceConnectionState]);\n\n if (handleId !== this.handleId && (config.pc.iceConnectionState === 'completed' || config.pc.iceConnectionState === 'connected')) {\n var _decodeJanusDisplay9;\n\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay9 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay9 === void 0 ? void 0 : _decodeJanusDisplay9.userId,\n stream: config.stream,\n track: null,\n optional: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n\n config.pc.onicecandidate = event => {\n if (event.candidate == null || webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0) {\n config.iceDone = true;\n\n this._sendTrickleCandidate(handleId, {\n \"completed\": true\n }).catch(e => {\n this.emit('error', e);\n });\n } else {\n // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n var candidate = {\n \"candidate\": event.candidate.candidate,\n \"sdpMid\": event.candidate.sdpMid,\n \"sdpMLineIndex\": event.candidate.sdpMLineIndex\n };\n\n this._sendTrickleCandidate(handleId, candidate).catch(e => {\n this.emit('error', e);\n });\n }\n };\n\n if (enableOntrack) {\n config.pc.ontrack = event => {\n if (!event.streams) return; //config.stream = event.streams[0];\n\n if (!config.stream) {\n config.stream = new MediaStream();\n }\n\n if (event.track) {\n var _decodeJanusDisplay10;\n\n let mid = event.transceiver ? event.transceiver.mid : event.track.id;\n config.stream.addTrack(event.track);\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n mid,\n id: handle.handleId,\n userId: (_decodeJanusDisplay10 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay10 === void 0 ? void 0 : _decodeJanusDisplay10.userId,\n stream: config.stream,\n track: event.track,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n if (event.track.onended) return;\n\n event.track.onended = ev => {\n let mid = ev.target.id;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n mid = transceiver.mid;\n }\n\n if (config.stream) {\n var _decodeJanusDisplay11;\n\n config.stream && config.stream.removeTrack(ev.target);\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n mid,\n userId: (_decodeJanusDisplay11 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay11 === void 0 ? void 0 : _decodeJanusDisplay11.userId,\n stream: config.stream,\n track: ev.target,\n constructId: this.constructId,\n metaData: this.options.metaData,\n adding: false,\n removing: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n\n event.track.onmute = ev => {\n var _decodeJanusDisplay12;\n\n this._log('remoteTrackMuted', 'onmute');\n\n let mid = ev.target.id;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n mid = transceiver.mid;\n }\n\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n userId: (_decodeJanusDisplay12 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay12 === void 0 ? void 0 : _decodeJanusDisplay12.userId,\n stream: config.stream,\n kind: ev.target.kind,\n track: ev.target,\n muted: true\n });\n };\n\n event.track.onunmute = ev => {\n var _decodeJanusDisplay13;\n\n this._log('remoteTrackMuted', 'onunmute');\n\n let mid = ev.target.id;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n mid = transceiver.mid;\n }\n\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n userId: (_decodeJanusDisplay13 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay13 === void 0 ? void 0 : _decodeJanusDisplay13.userId,\n stream: config.stream,\n kind: ev.target.kind,\n track: ev.target,\n muted: false\n });\n };\n }\n };\n }\n }\n\n if (!config.dataChannel || !config.dataChannelOpen) {\n config.dataChannel = {};\n\n var onDataChannelMessage = event => {\n this._handleDataEvents(handleId, 'message', event.data);\n };\n\n var onDataChannelStateChange = event => {\n let label = event.target.label;\n let protocol = event.target.protocol;\n let state = config.dataChannel[label] ? config.dataChannel[label].readyState : \"null\";\n\n this._handleDataEvents(handleId, 'state', {\n state,\n label\n });\n }; //TODO: check this\n\n\n var onDataChannelError = error => {\n var _error$channel;\n\n this._handleDataEvents(handleId, 'error', {\n label: error === null || error === void 0 ? void 0 : (_error$channel = error.channel) === null || _error$channel === void 0 ? void 0 : _error$channel.label,\n error\n });\n };\n\n const createDataChannel = (label, protocol, incoming) => {\n let options = {\n ordered: true\n };\n\n if (!incoming) {\n if (protocol) {\n options = { ...options,\n protocol\n };\n }\n\n config.dataChannel[label] = config.pc.createDataChannel(label, options);\n } else {\n config.dataChannel[label] = incoming;\n }\n\n config.dataChannel[label].onmessage = onDataChannelMessage;\n config.dataChannel[label].onopen = onDataChannelStateChange;\n config.dataChannel[label].onclose = onDataChannelStateChange;\n config.dataChannel[label].onerror = onDataChannelError;\n };\n\n createDataChannel(this.defaultDataChannelLabel, null, null);\n\n config.pc.ondatachannel = function (event) {\n createDataChannel(event.channel.label, event.channel.protocol, event.channel);\n };\n }\n }\n\n _webrtcPeer(handleId, jsep) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'rtc peer']\n });\n }\n\n var config = handle.webrtcStuff;\n\n if (jsep !== undefined && jsep !== null) {\n if (config.pc === null) {\n this._log(\"No PeerConnection: if this is an answer, use createAnswer and not _webrtcPeer\");\n\n return Promise.resolve(null);\n }\n\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp; // Any trickle candidate we cached?\n\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n }\n\n config.candidates = [];\n } // Done\n\n\n return true;\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 22,\n message: 'rtc peer',\n data: [handleId, 'invalid jsep']\n });\n }\n }\n\n _iceRestart(handleId) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return;\n }\n\n var config = handle.webrtcStuff; // Already restarting;\n\n if (config.isIceRestarting) {\n return;\n }\n\n if (this.handleId === handleId) {\n this._log('Performing local ICE restart');\n\n config.isIceRestarting = true;\n let hasAudio = !!(config.stream && config.stream.getAudioTracks().length > 0);\n let hasVideo = !!(config.stream && config.stream.getVideoTracks().length > 0);\n\n this._createAO('offer', handleId, true, [hasAudio, false, hasVideo, false]).then(jsep => {\n if (!jsep) {\n return null;\n }\n\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"audio\": hasAudio,\n \"video\": hasVideo,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {})\n },\n jsep\n });\n }).then(r => {\n config.isIceRestarting = false;\n\n this._log('ICE restart success');\n }).catch(e => {\n config.isIceRestarting = false;\n this.emit('warning', {\n type: 'error',\n id: 28,\n message: 'iceRestart failed',\n data: e\n });\n });\n } else {\n this._log('Performing remote ICE restart', handleId);\n\n config.isIceRestarting = true;\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"restart\": true\n }\n }).then(() => {\n config.isIceRestarting = false;\n }).catch(() => {\n config.isIceRestarting = false;\n });\n }\n }\n\n _createAO() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'offer';\n let handleId = arguments.length > 1 ? arguments[1] : undefined;\n let iceRestart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let [audioSend, audioRecv, videoSend, videoRecv] = arguments.length > 3 ? arguments[3] : undefined;\n\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'createAO', type]\n });\n }\n\n let methodName = null;\n\n if (type === 'offer') {\n methodName = 'createOffer';\n } else {\n methodName = 'createAnswer';\n }\n\n var config = handle.webrtcStuff; // https://code.google.com/p/webrtc/issues/detail?id=3508\n\n var mediaConstraints = {};\n\n if (this.isUnifiedPlan) {\n var audioTransceiver = null,\n videoTransceiver = null;\n var transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (var i in transceivers) {\n var t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\" && t.stopped === false) {\n if (!audioTransceiver) audioTransceiver = t;\n continue;\n }\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\" && t.stopped === false) {\n if (!videoTransceiver) videoTransceiver = t;\n continue;\n }\n }\n } // Handle audio (and related changes, if any)\n\n\n if (!audioSend && !audioRecv) {\n // Audio disabled: have we removed it?\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"inactive\");\n } else {\n audioTransceiver.direction = \"inactive\";\n }\n }\n } else {\n // Take care of audio m-line\n if (audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendrecv\");\n } else {\n audioTransceiver.direction = \"sendrecv\";\n }\n }\n } else if (audioSend && !audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendonly\");\n } else {\n audioTransceiver.direction = \"sendonly\";\n }\n }\n } else if (!audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"recvonly\");\n } else {\n audioTransceiver.direction = \"recvonly\";\n }\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n audioTransceiver = config.pc.addTransceiver(\"audio\", {\n direction: \"recvonly\"\n });\n }\n }\n } // Handle video (and related changes, if any)\n\n\n if (!videoSend && !videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"inactive\");\n } else {\n videoTransceiver.direction = \"inactive\";\n }\n }\n } else {\n // Take care of video m-line\n if (videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendrecv\");\n } else {\n videoTransceiver.direction = \"sendrecv\";\n }\n }\n } else if (videoSend && !videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendonly\");\n } else {\n videoTransceiver.direction = \"sendonly\";\n }\n }\n } else if (!videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"recvonly\");\n } else {\n videoTransceiver.direction = \"recvonly\";\n }\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n videoTransceiver = config.pc.addTransceiver(\"video\", {\n direction: \"recvonly\"\n });\n }\n }\n }\n } else {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"firefox\" || webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"edge\") {\n mediaConstraints = {\n offerToReceiveAudio: audioRecv,\n offerToReceiveVideo: videoRecv\n };\n } else {\n mediaConstraints = {\n mandatory: {\n OfferToReceiveAudio: audioRecv,\n OfferToReceiveVideo: videoRecv\n }\n };\n }\n }\n\n if (iceRestart) {\n mediaConstraints[\"iceRestart\"] = true;\n }\n\n return config.pc[methodName](mediaConstraints).then(function (response) {\n config.mySdp = response.sdp;\n\n let _p = config.pc.setLocalDescription(response).catch(e => {\n return Promise.reject({\n type: 'warning',\n id: 24,\n message: 'setLocalDescription',\n data: [handleId, e]\n });\n });\n\n config.mediaConstraints = mediaConstraints;\n\n if (!config.iceDone && !config.trickle) {\n // Don't do anything until we have all candidates\n return Promise.resolve(null);\n } // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n\n\n var jsep = {\n \"type\": response.type,\n \"sdp\": response.sdp\n };\n return _p.then(() => jsep);\n }, e => {\n return Promise.reject({\n type: 'warning',\n id: 25,\n message: methodName,\n data: [handleId, e]\n });\n });\n }\n\n _publishRemote(handleId, jsep) {\n let handle = this._getHandle(handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'publish remote participant']\n });\n }\n\n this._webrtc(handleId, true);\n\n let config = handle.webrtcStuff;\n\n if (jsep) {\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp; // Any trickle candidate we cached?\n\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n\n if (!candidate || candidate.completed === true) {\n // end-of-candidates\n config.pc.addIceCandidate(null);\n } else {\n // New candidate\n config.pc.addIceCandidate(candidate);\n }\n }\n\n config.candidates = [];\n } // Create the answer now\n\n\n return this._createAO('answer', handleId, false, [false, true, false, true]).then(_jsep => {\n if (!_jsep) {\n this.emit('error', {\n type: 'warning',\n id: 19,\n message: 'publish remote participant',\n data: [handleId, 'no jsep']\n });\n return Promise.resolve();\n }\n\n return this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"start\",\n ...(this.roomId && {\n \"room\": this.roomId\n }),\n ...(this.pin && {\n pin: this.pin\n })\n },\n \"jsep\": _jsep\n });\n });\n }, e => Promise.reject({\n type: 'warning',\n id: 23,\n message: 'setRemoteDescription',\n data: [handleId, e]\n }));\n } else {\n return Promise.resolve();\n }\n } //Public methods\n\n\n publishLocal(stream) {\n let {\n keepAudio = false,\n keepVideo = false\n } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (this.isDisconnecting || !this.isConnected) {\n return Promise.reject({\n type: 'warning',\n id: 18,\n message: 'Either not connected or disconnecting'\n });\n }\n\n this.emit('publishing', true);\n\n let handle = this._getHandle(this.handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 21,\n message: 'no local id, connect before publishing',\n data: null\n });\n }\n\n this._webrtc(this.handleId);\n\n let config = handle.webrtcStuff;\n\n if (stream) {\n if (!config.stream) {\n config.stream = stream;\n stream.getTracks().forEach(function (track) {\n config.pc.addTrack(track, stream);\n });\n } else {\n /* UPDATE Audio */\n let replaceAudio = stream.getAudioTracks().length;\n\n if (replaceAudio || !keepAudio) {\n //this will stop existing tracks\n let oldAudioStream = config.stream.getAudioTracks()[0];\n\n if (oldAudioStream) {\n config.stream.removeTrack(oldAudioStream);\n\n try {\n oldAudioStream.stop();\n } catch (e) {\n this._log(e);\n }\n }\n }\n\n if (config.pc.getSenders() && config.pc.getSenders().length) {\n if (replaceAudio && this.isUnifiedPlan) {//using replace\n } else {\n for (let index in config.pc.getSenders()) {\n let s = config.pc.getSenders()[index];\n\n if (s && s.track && s.track.kind === \"audio\") {\n config.pc.removeTrack(s);\n }\n }\n }\n }\n\n if (replaceAudio) {\n config.stream.addTrack(stream.getAudioTracks()[0]);\n var audioTransceiver = null;\n\n if (this.isUnifiedPlan) {\n let transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (let i in transceivers) {\n let t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\" && t.stopped === false) {\n audioTransceiver = t;\n break;\n }\n }\n }\n }\n\n if (audioTransceiver && audioTransceiver.sender) {\n audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);\n } else {\n config.pc.addTrack(stream.getAudioTracks()[0], stream);\n }\n }\n /* UPDATE Video */\n\n\n let replaceVideo = stream.getVideoTracks().length;\n\n if (replaceVideo || !keepVideo) {\n let oldVideoStream = config.stream.getVideoTracks()[0];\n\n if (oldVideoStream) {\n config.stream.removeTrack(oldVideoStream);\n\n try {\n oldVideoStream.stop();\n } catch (e) {\n this._log(e);\n }\n }\n }\n\n if (config.pc.getSenders() && config.pc.getSenders().length) {\n if (replaceVideo && this.isUnifiedPlan) {//using replace\n } else {\n for (let index in config.pc.getSenders()) {\n let s = config.pc.getSenders()[index];\n\n if (s && s.track && s.track.kind === \"video\") {\n config.pc.removeTrack(s);\n }\n }\n }\n }\n\n if (replaceVideo) {\n config.stream.addTrack(stream.getVideoTracks()[0]);\n var videoTransceiver = null;\n\n if (this.isUnifiedPlan) {\n let transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (let i in transceivers) {\n let t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" && t.stopped === false || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\" && t.stopped === false) {\n videoTransceiver = t;\n break;\n }\n }\n }\n }\n\n if (videoTransceiver && videoTransceiver.sender) {\n //TODO: check if t.stopped === false still gets us videoTransceiver\n videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);\n } else {\n config.pc.addTrack(stream.getVideoTracks()[0], stream);\n }\n }\n }\n }\n\n let hasAudio = !!(stream && stream.getAudioTracks().length > 0);\n let hasVideo = !!(stream && stream.getVideoTracks().length > 0);\n let isAudioMuted = !stream || stream.getAudioTracks().length === 0 || !stream.getAudioTracks()[0].enabled;\n let isVideoMuted = !stream || stream.getVideoTracks().length === 0 || !stream.getVideoTracks()[0].enabled;\n this.isAudioEnabed = hasAudio;\n this.isVideoEnabled = hasVideo;\n this.isAudioMuted = isAudioMuted;\n this.isVideoMuted = isVideoMuted;\n return this._createAO('offer', this.handleId, false, [hasAudio, false, hasVideo, false]).then(jsep => {\n if (!jsep) {\n return null;\n } //HOTFIX: Temporary fix for Safari 13\n\n\n if (jsep.sdp && jsep.sdp.indexOf(\"\\r\\na=ice-ufrag\") === -1) {\n jsep.sdp = jsep.sdp.replace(\"\\na=ice-ufrag\", \"\\r\\na=ice-ufrag\");\n }\n\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"configure\",\n \"audio\": hasAudio,\n \"video\": hasVideo,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {})\n },\n jsep\n });\n }).then(r => {\n if (this._isDataChannelOpen) {\n return Promise.resolve(r);\n } else {\n return new Promise((resolve, reject) => {\n let dataChannelTimeoutId = null;\n\n let _resolve = val => {\n if (val) {\n clearTimeout(dataChannelTimeoutId);\n\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n\n this.off('dataChannel', _resolve, this);\n resolve(this);\n }\n };\n\n let _rejectTimeout = () => {\n this.off('dataChannel', _resolve, this);\n\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n\n reject({\n type: 'error',\n id: 27,\n message: 'Data channel did not open',\n data: null\n });\n };\n\n let _rejectAbort = () => {\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n\n clearTimeout(dataChannelTimeoutId);\n this.off('dataChannel', _resolve, this);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n\n dataChannelTimeoutId = setTimeout(_rejectTimeout, 5000);\n\n this._abortController.signal.addEventListener('abort', _rejectAbort);\n\n this.on('dataChannel', _resolve, this);\n });\n }\n }).then(r => {\n this.isPublished = true;\n\n if (config.stream) {\n let tracks = config.stream.getTracks();\n tracks.forEach(track => {\n // used as a flag to not emit tracks that been already emitted\n if (!track.onended) {\n var _decodeJanusDisplay14;\n\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay14 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay14 === void 0 ? void 0 : _decodeJanusDisplay14.userId,\n track,\n stream: config.stream,\n adding: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n\n track.onended = ev => {\n var _decodeJanusDisplay15;\n\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay15 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay15 === void 0 ? void 0 : _decodeJanusDisplay15.userId,\n track: ev.target,\n stream: config.stream,\n adding: false,\n removing: true,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n };\n }\n });\n } else {\n var _decodeJanusDisplay16;\n\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay16 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay16 === void 0 ? void 0 : _decodeJanusDisplay16.userId,\n stream: null,\n adding: false,\n constructId: this.constructId,\n metaData: this.options.metaData,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n }\n\n this.emit('published', {\n status: true,\n hasStream: !!config.stream\n });\n this.emit('publishing', false);\n this.emit('localHasVideo', hasVideo);\n this.emit('localHasAudio', hasAudio);\n this.emit('localMuted', {\n type: 'video',\n value: isVideoMuted\n });\n this.emit('localMuted', {\n type: 'audio',\n value: isAudioMuted\n });\n return r;\n }).catch(e => {\n this.emit('publishing', false);\n return Promise.reject(e);\n });\n }\n\n unpublishLocal() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this.isPublished ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"unpublish\"\n }\n }, dontWait).finally(r => {\n this.isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n return r;\n }) : Promise.resolve();\n }\n\n toggleAudio() {\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let mid = arguments.length > 1 ? arguments[1] : undefined;\n\n let handle = this._getHandle(this.handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n }\n\n let config = handle.webrtcStuff;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.sender && t.sender.track && t.receiver.track.kind === \"audio\" && t.stopped === false && (mid ? t.mid === mid : true));\n\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n\n this.isAudioMuted = !transceiver || !transceiver.sender.track.enabled;\n } else {\n if (config.stream && config.stream.getAudioTracks().length) {\n config.stream.getAudioTracks()[0].enabled = value !== null ? !!value : !config.stream.getAudioTracks()[0].enabled;\n }\n\n this.isAudioMuted = config.stream.getAudioTracks().length === 0 || !config.stream.getAudioTracks()[0].enabled;\n }\n\n this.emit('localMuted', {\n type: 'audio',\n value: this.isAudioMuted,\n mid\n });\n }\n\n toggleVideo() {\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let mid = arguments.length > 1 ? arguments[1] : undefined;\n\n let handle = this._getHandle(this.handleId);\n\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n }\n\n let config = handle.webrtcStuff;\n\n if (this.isUnifiedPlan) {\n let transceiver = config.pc.getTransceivers().find(t => t.sender && t.sender.track && t.receiver.track.kind === \"video\" && t.stopped === false && (mid ? t.mid === mid : true));\n\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n\n this.isVideoMuted = !transceiver || !transceiver.sender.track.enabled;\n } else {\n if (config.stream && config.stream.getVideoTracks().length) {\n config.stream.getVideoTracks()[0].enabled = value !== null ? !!value : !config.stream.getVideoTracks()[0].enabled;\n }\n\n this.isVideoMuted = config.stream.getVideoTracks().length === 0 || !config.stream.getVideoTracks()[0].enabled;\n }\n\n this.emit('localMuted', {\n type: 'video',\n value: this.isVideoMuted,\n mid\n });\n }\n\n setInstructorId() {\n let instructorId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n this._instuctorId = instructorId;\n this.emit('instructorId', this._instuctorId);\n return this._instuctorId;\n }\n\n setObserverIds() {\n let observerIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n this._observerIds = observerIds;\n this.emit('observerIds', this._observerIds);\n return this._observerIds;\n }\n\n setTalkbackIds() {\n let talkbackIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n this._talkbackIds = talkbackIds;\n this.emit('talkbackIds', this._talkbackIds);\n return this._talkbackIds;\n }\n\n setRoomType() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'watchparty';\n this._roomType = type;\n return this._roomType;\n }\n\n}\n\n_defineProperty(RoomSession, \"sessionTypes\", {\n 'reactooroom': 'janus.plugin.reactooroom',\n 'streaming': 'janus.plugin.streaming'\n});\n\n_defineProperty(RoomSession, \"subscriptionRules\", {\n participant: {\n \"watchparty\": ['participant', 'talkback'],\n \"commentary\": ['participant', 'talkback'],\n \"videowall\": ['instructor', 'observer', 'talkback'],\n \"videowall-queue\": ['instructor', 'observer', 'talkback'],\n \"videowall-queue-video\": ['instructor', 'observer', 'talkback']\n },\n monitor: {\n \"watchparty\": ['participant'],\n \"commentary\": ['participant'],\n \"videowall\": ['instructor', 'participant'],\n \"videowall-queue\": ['instructor', 'participant'],\n \"videowall-queue-video\": ['instructor', 'participant']\n },\n talkback: {\n \"watchparty\": ['participant'],\n \"commentary\": ['participant'],\n \"videowall\": ['instructor', 'participant'],\n \"videowall-queue\": ['instructor', 'participant'],\n \"videowall-queue-video\": ['instructor', 'participant']\n },\n observer: {\n \"watchparty\": ['participant'],\n \"commentary\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n instructor: {\n \"watchparty\": [],\n \"commentary\": [],\n \"videowall\": [],\n \"videowall-queue\": [],\n \"videowall-queue-video\": []\n }\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Room);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/wt-room.js?");
|
|
9396
9396
|
|
|
9397
9397
|
/***/ }),
|
|
9398
9398
|
|
|
@@ -9400,11 +9400,11 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var webr
|
|
|
9400
9400
|
/*!*********************************!*\
|
|
9401
9401
|
!*** ./src/modules/wt-utils.js ***!
|
|
9402
9402
|
\*********************************/
|
|
9403
|
-
/*! exports provided: wait, getBrowserFingerprint, generateUUID, setExactTimeout, clearExactTimeout */
|
|
9403
|
+
/*! exports provided: wait, getBrowserFingerprint, generateUUID, decodeJanusDisplay, setExactTimeout, clearExactTimeout */
|
|
9404
9404
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
9405
9405
|
|
|
9406
9406
|
"use strict";
|
|
9407
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wait\", function() { return wait; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getBrowserFingerprint\", function() { return getBrowserFingerprint; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"generateUUID\", function() { return generateUUID; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"setExactTimeout\", function() { return setExactTimeout; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"clearExactTimeout\", function() { return clearExactTimeout; });\n/* harmony import */ var _fingerprintjs_fingerprintjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fingerprintjs/fingerprintjs */ \"./node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.js\");\n\n\nlet wait = function (ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n};\n\nlet fingerprint = _fingerprintjs_fingerprintjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"].load({\n monitoring: false\n});\n\nlet getBrowserFingerprint = function () {\n let instanceType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n let salt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';\n return fingerprint.then(fp => fp.get()).then(result => {\n const components = { ...result.components,\n instanceType: {\n value: instanceType + '_' + salt\n }\n };\n return [8, 13, 18, 23].reduce((acc, cur) => {\n return acc.slice(0, cur) + '-' + acc.slice(cur);\n }, _fingerprintjs_fingerprintjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hashComponents(components)).substring(0, 36);\n });\n};\n\nlet generateUUID = function () {\n var d = Date.now();\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n var r = (d + Math.random() * 16) % 16 | 0;\n d = Math.floor(d / 16);\n return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);\n });\n};\n\nconst setExactTimeout = function (callback, duration, resolution) {\n const start = new Date().getTime();\n const timeout = setInterval(function () {\n if (new Date().getTime() - start > duration) {\n callback();\n clearInterval(timeout);\n }\n }, resolution);\n return timeout;\n};\n\nconst clearExactTimeout = function (timeout) {\n clearInterval(timeout);\n};\n\n\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/wt-utils.js?");
|
|
9407
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wait\", function() { return wait; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getBrowserFingerprint\", function() { return getBrowserFingerprint; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"generateUUID\", function() { return generateUUID; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"decodeJanusDisplay\", function() { return decodeJanusDisplay; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"setExactTimeout\", function() { return setExactTimeout; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"clearExactTimeout\", function() { return clearExactTimeout; });\n/* harmony import */ var _fingerprintjs_fingerprintjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fingerprintjs/fingerprintjs */ \"./node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.js\");\n\n\nlet wait = function (ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n};\n\nlet fingerprint = _fingerprintjs_fingerprintjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"].load({\n monitoring: false\n});\n\nlet getBrowserFingerprint = function () {\n let instanceType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n let salt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';\n return fingerprint.then(fp => fp.get()).then(result => {\n const components = { ...result.components,\n instanceType: {\n value: instanceType + '_' + salt\n }\n };\n return [8, 13, 18, 23].reduce((acc, cur) => {\n return acc.slice(0, cur) + '-' + acc.slice(cur);\n }, _fingerprintjs_fingerprintjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hashComponents(components)).substring(0, 36);\n });\n};\n\nlet generateUUID = function () {\n var d = Date.now();\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n var r = (d + Math.random() * 16) % 16 | 0;\n d = Math.floor(d / 16);\n return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);\n });\n};\n\nconst janusDisplayDelimiter = ',';\n\nconst decodeJanusDisplay = display => {\n let output = {\n userId: null,\n role: \"participant\",\n start: Date.now(),\n displayName: \"?\"\n };\n\n if (display && typeof display === \"string\") {\n if (display.indexOf(janusDisplayDelimiter) >= 0) {\n let values = display.split(new RegExp(\"\\\\\".concat(janusDisplayDelimiter, \"(?=(?:(?:[^\\\"]*\\\"){2})*[^\\\"]*$)\"), 'mi')).map(v => v && v.startsWith('\"') || v.endsWith('\"') ? v.substring(1, v.length - 1) : /^\\d+$/.test(v) ? parseInt(v) : v);\n Object.keys(output).forEach((key, i) => values[i] ? output[key] = values[i] : null);\n } else {\n output.userId = display;\n }\n\n return output;\n } else if (display && typeof display === \"object\") {\n return Object.assign({}, output, display);\n }\n\n return null;\n};\n\nconst setExactTimeout = function (callback, duration, resolution) {\n const start = new Date().getTime();\n const timeout = setInterval(function () {\n if (new Date().getTime() - start > duration) {\n callback();\n clearInterval(timeout);\n }\n }, resolution);\n return timeout;\n};\n\nconst clearExactTimeout = function (timeout) {\n clearInterval(timeout);\n};\n\n\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/wt-utils.js?");
|
|
9408
9408
|
|
|
9409
9409
|
/***/ }),
|
|
9410
9410
|
|